aboutsummaryrefslogtreecommitdiff
path: root/libs/kstd
diff options
context:
space:
mode:
Diffstat (limited to 'libs/kstd')
-rw-r--r--libs/kstd/CMakeLists.txt2
-rw-r--r--libs/kstd/include/kstd/bits/format/context.hpp17
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/bool.hpp4
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/char.hpp94
-rw-r--r--libs/kstd/include/kstd/bits/format/formatter/integral.hpp4
-rw-r--r--libs/kstd/include/kstd/bits/format/output_buffer.hpp32
-rw-r--r--libs/kstd/include/kstd/bits/format/specifiers.hpp6
-rw-r--r--libs/kstd/include/kstd/bits/format/vformat.hpp99
-rw-r--r--libs/kstd/include/kstd/format3
-rw-r--r--libs/kstd/include/kstd/string20
-rw-r--r--libs/kstd/include/kstd/vector2
-rw-r--r--libs/kstd/src/vformat.cpp209
-rw-r--r--libs/kstd/tests/src/format.cpp116
13 files changed, 594 insertions, 14 deletions
diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt
index 240118e..ced3138 100644
--- a/libs/kstd/CMakeLists.txt
+++ b/libs/kstd/CMakeLists.txt
@@ -21,6 +21,7 @@ target_sources("kstd" PRIVATE
"src/os/error.cpp"
"src/mutex.cpp"
+ "src/vformat.cpp"
)
file(GLOB_RECURSE KSTD_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/kstd/*")
@@ -43,6 +44,7 @@ if(CMAKE_CROSSCOMPILING)
else()
add_executable("kstd_tests"
"tests/src/flat_map.cpp"
+ "tests/src/format.cpp"
"tests/src/vector.cpp"
"tests/src/observer_ptr.cpp"
"tests/src/os_panic.cpp"
diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp
index 478a48f..7f392a0 100644
--- a/libs/kstd/include/kstd/bits/format/context.hpp
+++ b/libs/kstd/include/kstd/bits/format/context.hpp
@@ -4,6 +4,7 @@
// IWYU pragma: private, include <kstd/format>
#include "arg.hpp"
+#include "kstd/bits/format/output_buffer.hpp"
#include <kstd/os/error.hpp>
@@ -29,13 +30,14 @@ namespace kstd
struct format_context
{
- using writer_function = void(void *, std::string_view);
using format_args = std::span<format_arg const>;
-
- writer_function * writer{};
- void * user_data{};
format_args args{};
+ format_context(bits::format::output_buffer & buffer, format_args args)
+ : args{args}
+ , m_buffer{&buffer}
+ {}
+
[[nodiscard]] auto arg(std::size_t const id) const -> format_arg const &
{
if (id >= args.size())
@@ -47,13 +49,16 @@ namespace kstd
constexpr auto push(std::string_view string) -> void
{
- writer(user_data, string);
+ m_buffer->push(string);
}
constexpr auto push(char character) -> void
{
- writer(user_data, std::string_view(&character, 1));
+ m_buffer->push(character);
}
+
+ private:
+ bits::format::output_buffer * m_buffer;
};
} // namespace kstd
diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp
index 336e1b0..e371cec 100644
--- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp
+++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp
@@ -51,11 +51,11 @@ namespace kstd
auto const text = value ? std::string_view{"true"} : std::string_view{"false"};
auto final_width = 0uz;
- if (specifiers.width_mode == bits::format::width_mode::static_value)
+ if (specifiers.mode == bits::format::width_mode::static_value)
{
final_width = specifiers.width_value;
}
- else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id)
+ else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id)
{
auto const & arg = context.arg(specifiers.width_value);
final_width = bits::format::extrat_dynamic_width(arg);
diff --git a/libs/kstd/include/kstd/bits/format/formatter/char.hpp b/libs/kstd/include/kstd/bits/format/formatter/char.hpp
new file mode 100644
index 0000000..ddfefe5
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/formatter/char.hpp
@@ -0,0 +1,94 @@
+#ifndef KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP
+#define KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP
+
+#include "../context.hpp"
+#include "../error.hpp"
+#include "../formatter.hpp"
+#include "../parse_context.hpp"
+#include "../specifiers.hpp"
+#include "integral.hpp"
+
+#include <iterator>
+
+namespace kstd
+{
+
+ template<>
+ struct formatter<char> : formatter<unsigned char>
+ {
+ bits::format::specifiers specifiers{};
+
+ constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator
+ {
+ specifiers = bits::format::parse_format_specifiers(context);
+
+ auto it = context.begin();
+ auto const end = context.end();
+
+ if (specifiers.alternative_form || specifiers.zero_pad || specifiers.sign != bits::format::sign_mode::none)
+ {
+ bits::format::error("Invalid format specifiers for 'char'");
+ }
+
+ if (it != end && *it != '}')
+ {
+ if (*it == 'c' || *it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X')
+ {
+ specifiers.type = *it;
+ std::advance(it, 1);
+ }
+ else
+ {
+ bits::format::error("Invalid type specifier for char.");
+ }
+ }
+
+ if (it != end && *it != '}')
+ {
+ bits::format::error("Missing terminating '}' in format string.");
+ }
+
+ return it;
+ }
+
+ auto format(char value, format_context & context) const -> void
+ {
+ if (specifiers.type == '\0' || specifiers.type == 'c')
+ {
+ auto final_width = 0uz;
+
+ if (specifiers.mode == bits::format::width_mode::static_value)
+ {
+ final_width = specifiers.width_value;
+ }
+ else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id)
+ {
+ auto const & arg = context.arg(specifiers.width_value);
+ final_width = bits::format::extrat_dynamic_width(arg);
+ }
+
+ auto padding =
+ bits::format::calculate_format_padding(final_width, 1, specifiers.align, bits::format::alignment::left);
+
+ for (auto i = 0uz; i < padding.left; ++i)
+ {
+ context.push(specifiers.fill);
+ }
+
+ context.push(value);
+
+ for (auto i = 0uz; i < padding.right; ++i)
+ {
+ context.push(specifiers.fill);
+ }
+ }
+ else
+ {
+ formatter<unsigned char>::format(static_cast<unsigned char>(value), context);
+ }
+ }
+ };
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp
index 4912a44..e5a234a 100644
--- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp
+++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp
@@ -64,11 +64,11 @@ namespace kstd
auto format(T value, format_context & context) const -> void
{
auto final_width = 0uz;
- if (specifiers.width_mode == bits::format::width_mode::static_value)
+ if (specifiers.mode == bits::format::width_mode::static_value)
{
final_width = specifiers.width_value;
}
- else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id)
+ else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id)
{
auto const & arg = context.arg(specifiers.width_value);
final_width = bits::format::extrat_dynamic_width(arg);
diff --git a/libs/kstd/include/kstd/bits/format/output_buffer.hpp b/libs/kstd/include/kstd/bits/format/output_buffer.hpp
new file mode 100644
index 0000000..fd7a2b4
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/output_buffer.hpp
@@ -0,0 +1,32 @@
+#ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP
+#define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP
+
+// IWYU pragma: private, include <kstd/format>
+
+#include <string_view>
+
+namespace kstd::bits::format
+{
+
+ //! An abstract interface for formatted output buffers.
+ //!
+ //! This interface is intended to be use for functions dealing with string formatting, like the print and the format
+ //! family.
+ struct output_buffer
+ {
+ virtual ~output_buffer() = default;
+
+ //! Push a text segment into the buffer.
+ //!
+ //! @param text The text segment to push.
+ virtual auto push(std::string_view text) -> void = 0;
+
+ //! Push a single character into the buffer.
+ //!
+ //! @param character The character to push into the buffer.
+ virtual auto push(char character) -> void = 0;
+ };
+
+} // namespace kstd::bits::format
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/format/specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp
index 9bc66c7..18c6f66 100644
--- a/libs/kstd/include/kstd/bits/format/specifiers.hpp
+++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp
@@ -43,7 +43,7 @@ namespace kstd::bits::format
bool alternative_form{};
bool zero_pad{};
- width_mode width_mode{};
+ width_mode mode{};
std::size_t width_value{};
char type{};
};
@@ -133,7 +133,7 @@ namespace kstd::bits::format
if (it != end && *it == '{')
{
- specs.width_mode = width_mode::dynamic_argument_id;
+ specs.mode = width_mode::dynamic_argument_id;
std::advance(it, 1);
auto argument_id = 0uz;
@@ -160,7 +160,7 @@ namespace kstd::bits::format
}
else if (it != end && *it >= '0' && *it <= '9')
{
- specs.width_mode = width_mode::static_value;
+ specs.mode = width_mode::static_value;
while (it != end && *it >= '0' && *it <= '9')
{
specs.width_value = specs.width_value * 10 + static_cast<std::size_t>(*it - '0');
diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp
new file mode 100644
index 0000000..4fec7dd
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/format/vformat.hpp
@@ -0,0 +1,99 @@
+#ifndef KSTD_BITS_FORMAT_VFORMAT_HPP
+#define KSTD_BITS_FORMAT_VFORMAT_HPP
+
+// IWYU pragma: private, include <kstd/format>
+
+#include <kstd/bits/format/args.hpp>
+#include <kstd/bits/format/output_buffer.hpp>
+#include <kstd/bits/format/string.hpp>
+#include <kstd/string>
+
+#include <algorithm>
+#include <iterator>
+#include <string_view>
+#include <type_traits>
+
+namespace kstd
+{
+
+ namespace bits::format
+ {
+ //! Format a string with the given arguments into the given buffer.
+ //!
+ //! External implementations of `vprint` may call this function.
+ //!
+ //! @param buffer The buffer to format into.
+ //! @param format The format string to use.
+ //! @param args The arguments for the format string.
+ auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void;
+
+ struct string_writer : output_buffer
+ {
+ auto push(std::string_view text) -> void override;
+ auto push(char character) -> void override;
+
+ auto release() -> string &&;
+
+ private:
+ string m_result{};
+ };
+
+ template<std::output_iterator<char> Output>
+ struct iterator_writer : output_buffer
+ {
+ explicit iterator_writer(Output iterator)
+ : m_output{iterator}
+ {}
+
+ auto iterator() const -> Output
+ {
+ return m_output;
+ }
+
+ auto push(std::string_view text) -> void override
+ {
+ m_output = std::ranges::copy(text, m_output).out;
+ }
+
+ auto push(char character) -> void override
+ {
+ *m_output++ = character;
+ }
+
+ private:
+ Output m_output{};
+ };
+
+ } // namespace bits::format
+
+ //! Format a given string with the provided arguments.
+ //!
+ //! @param format The format string.
+ //! @param args The arguments for the format string.
+ //! @return A new string containing the result of the format operation.
+ template<typename... ArgumentTypes>
+ auto format(format_string<std::type_identity_t<ArgumentTypes>...> format, ArgumentTypes &&... args) -> string
+ {
+ auto buffer = bits::format::string_writer{};
+ bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward<ArgumentTypes>(args)...).args);
+ return buffer.release();
+ }
+
+ //! Format a given string with the provided arguments.
+ //!
+ //! @param iterator The iterator to write to.
+ //! @param format The format string.
+ //! @param args The arguments for the format string.
+ //! @return An iterator past the last element written.
+ template<std::output_iterator<char> Output, typename... ArgumentTypes>
+ auto format_to(Output iterator, format_string<std::type_identity_t<ArgumentTypes>...> format,
+ ArgumentTypes &&... args) -> Output
+ {
+ auto buffer = bits::format::iterator_writer{iterator};
+ bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward<ArgumentTypes>(args)...).args);
+ return buffer.iterator();
+ }
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format
index adee5f8..047ea5c 100644
--- a/libs/kstd/include/kstd/format
+++ b/libs/kstd/include/kstd/format
@@ -7,13 +7,16 @@
#include "bits/format/formatter.hpp" // IWYU pragma: export
#include "bits/format/formatter/bool.hpp" // IWYU pragma: export
#include "bits/format/formatter/byte.hpp" // IWYU pragma: export
+#include "bits/format/formatter/char.hpp" // IWYU pragma: export
#include "bits/format/formatter/cstring.hpp" // IWYU pragma: export
#include "bits/format/formatter/integral.hpp" // IWYU pragma: export
#include "bits/format/formatter/ordering.hpp" // IWYU pragma: export
#include "bits/format/formatter/pointer.hpp" // IWYU pragma: export
#include "bits/format/formatter/range.hpp" // IWYU pragma: export
#include "bits/format/formatter/string_view.hpp" // IWYU pragma: export
+#include "bits/format/output_buffer.hpp" // IWYU pragma: export
#include "bits/format/parse_context.hpp" // IWYU pragma: export
#include "bits/format/string.hpp" // IWYU pragma: export
+#include "bits/format/vformat.hpp" // IWYU pragma: export
#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string
index 4ce19ce..58c4a08 100644
--- a/libs/kstd/include/kstd/string
+++ b/libs/kstd/include/kstd/string
@@ -347,6 +347,26 @@ namespace kstd
return !(lhs == rhs);
}
+ [[nodiscard]] constexpr auto inline operator==(string const & lhs, char const * rhs) -> bool
+ {
+ return lhs.view() == std::string_view{rhs};
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(string const & lhs, char const * rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
+ [[nodiscard]] constexpr auto inline operator==(char const * lhs, string const & rhs) -> bool
+ {
+ return std::string_view{lhs} == rhs.view();
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(char const * lhs, string const & rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
template<>
struct formatter<string> : formatter<std::string_view>
{
diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector
index e51cbac..79593c6 100644
--- a/libs/kstd/include/kstd/vector
+++ b/libs/kstd/include/kstd/vector
@@ -111,7 +111,7 @@ namespace kstd
allocator_type const & allocator =
allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>)
: m_allocator{allocator}
- , m_size{std::ranges::distance(first, last)}
+ , m_size{static_cast<std::size_t>(std::ranges::distance(first, last))}
, m_capacity{m_size}
, m_data{allocate_n(m_capacity)}
{
diff --git a/libs/kstd/src/vformat.cpp b/libs/kstd/src/vformat.cpp
new file mode 100644
index 0000000..b7c5121
--- /dev/null
+++ b/libs/kstd/src/vformat.cpp
@@ -0,0 +1,209 @@
+#include <kstd/format>
+#include <kstd/string>
+
+#include <cstddef>
+#include <iterator>
+#include <string_view>
+#include <utility>
+
+namespace kstd::bits::format
+{
+
+ auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void
+ {
+ auto context = kstd::format_context{buffer, args};
+ auto parse_context = kstd::format_parse_context{format, args.size()};
+
+ auto it = parse_context.begin();
+ auto end = parse_context.end();
+
+ while (it != end)
+ {
+ if (*it != '{' && *it != '}')
+ {
+ auto start = it;
+ while (it != end && *it != '{' && *it != '}')
+ {
+ std::advance(it, 1);
+ }
+ parse_context.advance_to(it);
+ context.push(std::string_view(start, it - start));
+ continue;
+ }
+
+ if (*it == '{')
+ {
+ std::advance(it, 1);
+ if (it != end && *it == '{')
+ {
+ context.push('{');
+ std::advance(it, 1);
+ parse_context.advance_to(it);
+ continue;
+ }
+
+ parse_context.advance_to(it);
+ auto index = 0uz;
+
+ if (it != end && *it >= '0' && *it <= '9')
+ {
+ while (it != end && *it >= '0' && *it <= '9')
+ {
+ index = index * 10 + static_cast<std::size_t>(*it - '0');
+ std::advance(it, 1);
+ }
+ parse_context.check_arg_id(index);
+ }
+ else
+ {
+ index = parse_context.next_arg_id();
+ }
+
+ if (it != end && *it == ':')
+ {
+ std::advance(it, 1);
+ }
+
+ parse_context.advance_to(it);
+
+ if (index < args.size())
+ {
+ auto const & arg = args[index];
+ switch (arg.type)
+ {
+ case kstd::bits::format::arg_type::boolean:
+ {
+ auto fmt = kstd::formatter<bool>{};
+ auto const parsed = fmt.parse(parse_context);
+ parse_context.advance_to(parsed);
+ fmt.format(arg.value.boolean, context);
+ break;
+ }
+ case kstd::bits::format::arg_type::character:
+ {
+ auto fmt = kstd::formatter<char>{};
+ auto const parsed = fmt.parse(parse_context);
+ parse_context.advance_to(parsed);
+ fmt.format(arg.value.character, context);
+ break;
+ }
+ case kstd::bits::format::arg_type::integer:
+ {
+ auto fmt = kstd::formatter<long long>{};
+ auto const parsed = fmt.parse(parse_context);
+ parse_context.advance_to(parsed);
+ fmt.format(arg.value.integer, context);
+ break;
+ }
+ case kstd::bits::format::arg_type::unsigned_integer:
+ {
+ auto fmt = kstd::formatter<unsigned long long>{};
+ auto const parsed = fmt.parse(parse_context);
+ parse_context.advance_to(parsed);
+ fmt.format(arg.value.unsigned_integer, context);
+ break;
+ }
+ case kstd::bits::format::arg_type::string_view:
+ {
+ auto fmt = kstd::formatter<std::string_view>{};
+ auto const parsed = fmt.parse(parse_context);
+ parse_context.advance_to(parsed);
+ fmt.format(arg.value.string_view, context);
+ break;
+ }
+ case kstd::bits::format::arg_type::c_string:
+ {
+ auto fmt = kstd::formatter<char const *>{};
+ auto const parsed = fmt.parse(parse_context);
+ parse_context.advance_to(parsed);
+ fmt.format(arg.value.c_string, context);
+ break;
+ }
+ case kstd::bits::format::arg_type::pointer:
+ {
+ auto fmt = kstd::formatter<void const *>{};
+ auto const parsed = fmt.parse(parse_context);
+ parse_context.advance_to(parsed);
+ fmt.format(arg.value.pointer, context);
+ break;
+ }
+ case kstd::bits::format::arg_type::user_defined:
+ {
+ if (arg.value.user_defined.format)
+ {
+ arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context);
+ }
+ else
+ {
+ context.push("{?}");
+ }
+ break;
+ }
+ default:
+ {
+ context.push("{fmt-err: unknown-type}");
+ break;
+ }
+ }
+ }
+ else
+ {
+ context.push("{fmt-err: bound}");
+ }
+
+ it = parse_context.begin();
+
+ if (it != end && *it == '}')
+ {
+ std::advance(it, 1);
+ parse_context.advance_to(it);
+ }
+ else
+ {
+ context.push("{fmt-err: unconsumed}");
+ while (it != end && *it != '}')
+ {
+ std::advance(it, 1);
+ }
+
+ if (it != end)
+ {
+ std::advance(it, 1);
+ parse_context.advance_to(it);
+ }
+ }
+ }
+ else if (*it == '}')
+ {
+ std::advance(it, 1);
+ if (it != end && *it == '}')
+ {
+ context.push('}');
+ std::advance(it, 1);
+ parse_context.advance_to(it);
+ }
+ else
+ {
+ context.push("{fmt-err: unescaped}");
+ parse_context.advance_to(it);
+ }
+ }
+ }
+ }
+
+ auto string_writer::push(std::string_view text) -> void
+ {
+ m_result.append(text);
+ }
+
+ auto string_writer::push(char character) -> void
+ {
+ m_result.push_back(character);
+ }
+
+ auto string_writer::release() -> string &&
+ {
+ return std::move(m_result);
+ }
+
+} // namespace kstd::bits::format \ No newline at end of file
diff --git a/libs/kstd/tests/src/format.cpp b/libs/kstd/tests/src/format.cpp
new file mode 100644
index 0000000..73c8102
--- /dev/null
+++ b/libs/kstd/tests/src/format.cpp
@@ -0,0 +1,116 @@
+#include <kstd/flat_map>
+#include <kstd/format>
+#include <kstd/tests/os_panic.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <iterator>
+#include <sstream>
+
+SCENARIO("Formatting to a new string", "[format]")
+{
+ GIVEN("a format string without any placeholders")
+ {
+ auto const & fmt = "This is a test";
+
+ WHEN("calling format with without any arguments.")
+ {
+ auto result = kstd::format(fmt);
+
+ THEN("the result is the unmodified string")
+ {
+ REQUIRE(result == "This is a test");
+ }
+ }
+
+ WHEN("calling format with additional arguments")
+ {
+ auto result = kstd::format(fmt, 1, 2, 3);
+
+ THEN("the result is the unmodified string")
+ {
+ REQUIRE(result == "This is a test");
+ }
+ }
+ }
+
+ GIVEN("a format string with placeholders")
+ {
+ auto const & fmt = "Here are some placeholders: {} {} {}";
+
+ WHEN("calling format with the same number of arguments as there are placeholders")
+ {
+ auto result = kstd::format(fmt, 1, true, 'a');
+
+ THEN("the result is the formatted string")
+ {
+ REQUIRE(result == "Here are some placeholders: 1 true a");
+ }
+ }
+
+ WHEN("calling format with too many arguments")
+ {
+ auto result = kstd::format(fmt, 2, false, 'b', 4, 5, 6);
+
+ THEN("the result is the formatted string")
+ {
+ REQUIRE(result == "Here are some placeholders: 2 false b");
+ }
+ }
+ }
+}
+
+SCENARIO("Formatting to an output iterator", "[format]")
+{
+ auto buffer = std::ostringstream{};
+
+ GIVEN("a format string without any placeholders")
+ {
+ auto const & fmt = "This is a test";
+
+ WHEN("calling format with without any arguments.")
+ {
+ kstd::format_to(std::ostream_iterator<char>{buffer}, fmt);
+
+ THEN("the unmodified string is written to the iterator")
+ {
+ REQUIRE(buffer.str() == "This is a test");
+ }
+ }
+
+ WHEN("calling format with additional arguments")
+ {
+ kstd::format_to(std::ostream_iterator<char>{buffer}, fmt, 1, 2, 3);
+
+ THEN("the unmodified string is written to the iterator")
+ {
+ REQUIRE(buffer.str() == "This is a test");
+ }
+ }
+ }
+
+ GIVEN("a format string with placeholders")
+ {
+ auto const & fmt = "Here are some placeholders: {} {} {}";
+
+ WHEN("calling format with the same number of arguments as there are placeholders")
+ {
+ kstd::format_to(std::ostream_iterator<char>{buffer}, fmt, 1, true, -100);
+
+ THEN("the formatted string is written to the iterator")
+ {
+ REQUIRE(buffer.str() == "Here are some placeholders: 1 true -100");
+ }
+ }
+
+ WHEN("calling format with too many arguments")
+ {
+ kstd::format_to(std::ostream_iterator<char>{buffer}, fmt, 2, false, -200, 4, 5, 6);
+
+ THEN("the formatted string is written to the iterator")
+ {
+ REQUIRE(buffer.str() == "Here are some placeholders: 2 false -200");
+ }
+ }
+ }
+} \ No newline at end of file