diff --git a/include/boost/mysql/format_sql.hpp b/include/boost/mysql/format_sql.hpp index 75b11548e..27be81543 100644 --- a/include/boost/mysql/format_sql.hpp +++ b/include/boost/mysql/format_sql.hpp @@ -122,6 +122,13 @@ struct formatter * instantiated directly - use \ref basic_format_context, instead. * Do not subclass it, either. * \n + * Conceptually, a format context contains: \n + * \li The result string. Output operations append characters to this output string. + * This type is agnostic to the output string type. + * \li Format options describing how to perform format operations. + * \li An error state that is set by output operations when they fail. + * If the error state is set, it will be propagated to \ref basic_format_context::get. + * \n * References to this class are useful when you need to manipulate * a format context without knowing the type of the actual context that will be used, * like when specializing \ref formatter. @@ -133,12 +140,6 @@ class format_context_base detail::output_string_ref output; format_options opts; error_code ec; - - void set_error(error_code new_ec) noexcept - { - if (!ec) - ec = new_ec; - } } impl_; friend struct detail::access; @@ -167,27 +168,49 @@ class format_context_base error_code get_error() const noexcept { return impl_.ec; } public: - /// TODO: document + /** + * \brief Adds raw SQL to the output string. + * \details + * Adds raw, unescaped SQL to the output string. By default, the passed SQL + * should be available at compile-time. Use \ref runtime if you need to use + * runtime values. + * + * \par Exception safety + * Basic guarantee. Memory allocations may throw. + * + * \par Object lifetimes + * The passed string is copied as required and doesn't need to be kept alive. + */ format_context_base& append_raw(constant_string_view sql) { impl_.output.append(sql.get()); return *this; } - /// TODO: document + /** + * \brief Formats a value and adds it to the output string. + * \details + * + * @tparam T + * @param v + * @return format_context_base& + */ template format_context_base& append_value(const T& v) { format_arg(detail::make_format_value(v)); return *this; } + + void add_error(error_code ec) noexcept + { + if (!impl_.ec) + impl_.ec = ec; + } }; /// (EXPERIMENTAL) Implements stream-like SQL formatting (TODO). -template -#ifdef BOOST_MYSQL_HAS_CONCEPTS - requires detail::output_string && std::move_constructible -#endif +template class basic_format_context : public format_context_base { OutputString output_{}; diff --git a/include/boost/mysql/impl/format_sql.ipp b/include/boost/mysql/impl/format_sql.ipp index 5a26ad42d..d25e9e67d 100644 --- a/include/boost/mysql/impl/format_sql.ipp +++ b/include/boost/mysql/impl/format_sql.ipp @@ -46,7 +46,8 @@ inline void append_identifier(string_view name, format_context_base& ctx) { auto& impl = access::get_impl(ctx); auto ec = detail::escape_string(name, impl.opts, '`', impl.output); - impl.set_error(ec); + if (ec) + ctx.add_error(ec); } template @@ -394,7 +395,8 @@ void boost::mysql::format_context_base::format_arg(detail::format_arg_value arg) else { error_code ec = detail::append_field_view(impl_.output, arg.data.fv, impl_.opts); - impl_.set_error(ec); + if (ec) + add_error(ec); } }