format_sql.hpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. //
  2. // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MYSQL_IMPL_FORMAT_SQL_HPP
  8. #define BOOST_MYSQL_IMPL_FORMAT_SQL_HPP
  9. #pragma once
  10. #include <boost/mysql/format_sql.hpp>
  11. #include <boost/mysql/detail/format_sql.hpp>
  12. #include <type_traits>
  13. namespace boost {
  14. namespace mysql {
  15. namespace detail {
  16. BOOST_MYSQL_DECL
  17. std::pair<bool, string_view> parse_range_specifiers(const char* spec_begin, const char* spec_end);
  18. // To use with arguments with a custom formatter
  19. template <class T>
  20. bool do_format_custom_formatter(
  21. const void* obj,
  22. const char* spec_begin,
  23. const char* spec_end,
  24. format_context_base& ctx
  25. )
  26. {
  27. formatter<T> fmt;
  28. const char* it = fmt.parse(spec_begin, spec_end);
  29. if (it != spec_end)
  30. {
  31. return false;
  32. }
  33. fmt.format(*static_cast<const T*>(obj), ctx);
  34. return true;
  35. }
  36. // To use with ranges
  37. template <class T>
  38. bool do_format_range(const void* obj, const char* spec_begin, const char* spec_end, format_context_base& ctx)
  39. {
  40. // Parse specifiers
  41. auto res = detail::parse_range_specifiers(spec_begin, spec_end);
  42. if (!res.first)
  43. return false;
  44. auto spec = runtime(res.second);
  45. // Retrieve the object. T here may be the actual type U or const U
  46. auto& value = *const_cast<T*>(static_cast<const T*>(obj));
  47. // Output the sequence
  48. bool is_first = true;
  49. for (auto it = std::begin(value); it != std::end(value); ++it)
  50. {
  51. if (!is_first)
  52. ctx.append_raw(", ");
  53. is_first = false;
  54. ctx.append_value(*it, spec);
  55. }
  56. return true;
  57. }
  58. // Make formattable_ref formattable
  59. inline formattable_ref_impl make_formattable_ref_custom(
  60. formattable_ref v,
  61. std::true_type // is format ref
  62. )
  63. {
  64. return access::get_impl(v);
  65. }
  66. // Make types with custom formatters formattable
  67. template <class T>
  68. formattable_ref_impl make_formattable_ref_custom(
  69. const T& v,
  70. std::false_type // is format ref
  71. )
  72. {
  73. // If you're getting an error here, it means that you're passing a type
  74. // that is not formattable to a SQL formatting function.
  75. static_assert(
  76. has_specialized_formatter<T>(),
  77. "T is not formattable. Please use a formattable type or specialize formatter<T> to make it "
  78. "formattable"
  79. );
  80. return {
  81. formattable_ref_impl::type_t::fn_and_ptr,
  82. formattable_ref_impl::fn_and_ptr{&v, &do_format_custom_formatter<T>}
  83. };
  84. }
  85. // Make ranges formattable
  86. template <class T>
  87. formattable_ref_impl make_formattable_ref_range(
  88. T&& v,
  89. std::true_type // formattable range
  90. )
  91. {
  92. // Although everything is passed as const void*, do_format_range
  93. // can bypass const-ness for non-const ranges (e.g. filter_view)
  94. return {
  95. formattable_ref_impl::type_t::fn_and_ptr,
  96. formattable_ref_impl::fn_and_ptr{&v, &do_format_range<typename std::remove_reference<T>::type>}
  97. };
  98. }
  99. template <class T>
  100. formattable_ref_impl make_formattable_ref_range(
  101. const T& v,
  102. std::false_type // formattable range
  103. )
  104. {
  105. return make_formattable_ref_custom(v, is_formattable_ref<T>());
  106. }
  107. // Used for types having is_writable_field<T>
  108. template <class T>
  109. formattable_ref_impl make_formattable_ref_writable(
  110. const T& v,
  111. std::true_type // is_writable_field
  112. )
  113. {
  114. // Only string types (and not field_views or optionals) support the string specifiers
  115. return {
  116. std::is_convertible<T, string_view>::value ? formattable_ref_impl::type_t::field_with_specs
  117. : formattable_ref_impl::type_t::field,
  118. to_field(v)
  119. };
  120. }
  121. template <class T>
  122. formattable_ref_impl make_formattable_ref_writable(
  123. T&& v,
  124. std::false_type // is_writable_field
  125. )
  126. {
  127. return make_formattable_ref_range(std::forward<T>(v), is_formattable_range<T>());
  128. }
  129. } // namespace detail
  130. } // namespace mysql
  131. } // namespace boost
  132. template <class T>
  133. boost::mysql::detail::formattable_ref_impl boost::mysql::detail::make_formattable_ref(T&& v)
  134. {
  135. // Hierarchy:
  136. // 1. writable field?
  137. // 2. formattable range?
  138. // 3. custom formatter or formattable_ref?
  139. return make_formattable_ref_writable(std::forward<T>(v), is_writable_field_ref<T>());
  140. }
  141. template <BOOST_MYSQL_FORMATTABLE... Formattable>
  142. void boost::mysql::format_sql_to(
  143. format_context_base& ctx,
  144. constant_string_view format_str,
  145. Formattable&&... args
  146. )
  147. {
  148. std::initializer_list<format_arg> args_il{
  149. {string_view(), std::forward<Formattable>(args)}
  150. ...
  151. };
  152. format_sql_to(ctx, format_str, args_il);
  153. }
  154. template <BOOST_MYSQL_FORMATTABLE... Formattable>
  155. std::string boost::mysql::format_sql(
  156. format_options opts,
  157. constant_string_view format_str,
  158. Formattable&&... args
  159. )
  160. {
  161. std::initializer_list<format_arg> args_il{
  162. {string_view(), std::forward<Formattable>(args)}
  163. ...
  164. };
  165. return format_sql(opts, format_str, args_il);
  166. }
  167. #endif