format_sql.hpp 5.1 KB

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