190 lines
5.0 KiB
C++
190 lines
5.0 KiB
C++
//
|
|
// Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
|
|
#ifndef BOOST_MYSQL_IMPL_FORMAT_SQL_HPP
|
|
#define BOOST_MYSQL_IMPL_FORMAT_SQL_HPP
|
|
|
|
#pragma once
|
|
|
|
#include <boost/mysql/format_sql.hpp>
|
|
|
|
#include <boost/mysql/detail/format_sql.hpp>
|
|
|
|
#include <type_traits>
|
|
|
|
namespace boost {
|
|
namespace mysql {
|
|
namespace detail {
|
|
|
|
BOOST_MYSQL_DECL
|
|
std::pair<bool, string_view> parse_range_specifiers(const char* spec_begin, const char* spec_end);
|
|
|
|
// To use with arguments with a custom formatter
|
|
template <class T>
|
|
bool do_format_custom_formatter(
|
|
const void* obj,
|
|
const char* spec_begin,
|
|
const char* spec_end,
|
|
format_context_base& ctx
|
|
)
|
|
{
|
|
formatter<T> fmt;
|
|
const char* it = fmt.parse(spec_begin, spec_end);
|
|
if (it != spec_end)
|
|
{
|
|
return false;
|
|
}
|
|
fmt.format(*static_cast<const T*>(obj), ctx);
|
|
return true;
|
|
}
|
|
|
|
// To use with ranges
|
|
template <class T>
|
|
bool do_format_range(const void* obj, const char* spec_begin, const char* spec_end, format_context_base& ctx)
|
|
{
|
|
// Parse specifiers
|
|
auto res = detail::parse_range_specifiers(spec_begin, spec_end);
|
|
if (!res.first)
|
|
return false;
|
|
auto spec = runtime(res.second);
|
|
|
|
// Retrieve the object. T here may be the actual type U or const U
|
|
auto& value = *const_cast<T*>(static_cast<const T*>(obj));
|
|
|
|
// Output the sequence
|
|
bool is_first = true;
|
|
for (auto it = std::begin(value); it != std::end(value); ++it)
|
|
{
|
|
if (!is_first)
|
|
ctx.append_raw(", ");
|
|
is_first = false;
|
|
ctx.append_value(*it, spec);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Make formattable_ref formattable
|
|
inline formattable_ref_impl make_formattable_ref_custom(
|
|
formattable_ref v,
|
|
std::true_type // is format ref
|
|
)
|
|
{
|
|
return access::get_impl(v);
|
|
}
|
|
|
|
// Make types with custom formatters formattable
|
|
template <class T>
|
|
formattable_ref_impl make_formattable_ref_custom(
|
|
const T& v,
|
|
std::false_type // is format ref
|
|
)
|
|
{
|
|
// If you're getting an error here, it means that you're passing a type
|
|
// that is not formattable to a SQL formatting function.
|
|
static_assert(
|
|
has_specialized_formatter<T>(),
|
|
"T is not formattable. Please use a formattable type or specialize formatter<T> to make it "
|
|
"formattable"
|
|
);
|
|
return {
|
|
formattable_ref_impl::type_t::fn_and_ptr,
|
|
formattable_ref_impl::fn_and_ptr{&v, &do_format_custom_formatter<T>}
|
|
};
|
|
}
|
|
|
|
// Make ranges formattable
|
|
template <class T>
|
|
formattable_ref_impl make_formattable_ref_range(
|
|
T&& v,
|
|
std::true_type // formattable range
|
|
)
|
|
{
|
|
// Although everything is passed as const void*, do_format_range
|
|
// can bypass const-ness for non-const ranges (e.g. filter_view)
|
|
return {
|
|
formattable_ref_impl::type_t::fn_and_ptr,
|
|
formattable_ref_impl::fn_and_ptr{&v, &do_format_range<typename std::remove_reference<T>::type>}
|
|
};
|
|
}
|
|
|
|
template <class T>
|
|
formattable_ref_impl make_formattable_ref_range(
|
|
const T& v,
|
|
std::false_type // formattable range
|
|
)
|
|
{
|
|
return make_formattable_ref_custom(v, is_formattable_ref<T>());
|
|
}
|
|
|
|
// Used for types having is_writable_field<T>
|
|
template <class T>
|
|
formattable_ref_impl make_formattable_ref_writable(
|
|
const T& v,
|
|
std::true_type // is_writable_field
|
|
)
|
|
{
|
|
// Only string types (and not field_views or optionals) support the string specifiers
|
|
return {
|
|
std::is_convertible<T, string_view>::value ? formattable_ref_impl::type_t::field_with_specs
|
|
: formattable_ref_impl::type_t::field,
|
|
to_field(v)
|
|
};
|
|
}
|
|
|
|
template <class T>
|
|
formattable_ref_impl make_formattable_ref_writable(
|
|
T&& v,
|
|
std::false_type // is_writable_field
|
|
)
|
|
{
|
|
return make_formattable_ref_range(std::forward<T>(v), is_formattable_range<T>());
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace mysql
|
|
} // namespace boost
|
|
|
|
template <class T>
|
|
boost::mysql::detail::formattable_ref_impl boost::mysql::detail::make_formattable_ref(T&& v)
|
|
{
|
|
// Hierarchy:
|
|
// 1. writable field?
|
|
// 2. formattable range?
|
|
// 3. custom formatter or formattable_ref?
|
|
return make_formattable_ref_writable(std::forward<T>(v), is_writable_field_ref<T>());
|
|
}
|
|
|
|
template <BOOST_MYSQL_FORMATTABLE... Formattable>
|
|
void boost::mysql::format_sql_to(
|
|
format_context_base& ctx,
|
|
constant_string_view format_str,
|
|
Formattable&&... args
|
|
)
|
|
{
|
|
std::initializer_list<format_arg> args_il{
|
|
{string_view(), std::forward<Formattable>(args)}
|
|
...
|
|
};
|
|
format_sql_to(ctx, format_str, args_il);
|
|
}
|
|
|
|
template <BOOST_MYSQL_FORMATTABLE... Formattable>
|
|
std::string boost::mysql::format_sql(
|
|
format_options opts,
|
|
constant_string_view format_str,
|
|
Formattable&&... args
|
|
)
|
|
{
|
|
std::initializer_list<format_arg> args_il{
|
|
{string_view(), std::forward<Formattable>(args)}
|
|
...
|
|
};
|
|
return format_sql(opts, format_str, args_il);
|
|
}
|
|
|
|
#endif
|