7#include <reflection-cpp/reflection.hpp>
11#include <source_location>
14#include <unordered_map>
19#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
20 #include <experimental/meta>
29 template <
typename T,
typename... Comps>
30 concept OneOf = (std::same_as<T, Comps> || ...);
33 constexpr auto AlwaysFalse = std::false_type::value;
35 constexpr auto Finally(
auto&& cleanupRoutine)
noexcept
40 std::remove_cvref_t<
decltype(cleanupRoutine)> cleanup;
46 return Finally { std::forward<decltype(cleanupRoutine)>(cleanupRoutine) };
52 template <
template <
typename...>
class T,
typename U>
53 struct is_specialization_of: std::false_type
57 template <
template <
typename...>
class T,
typename... Us>
58 struct is_specialization_of<T, T<Us...>>: std::true_type
63 struct MemberClassTypeHelper;
65 template <
typename M,
typename T>
66 struct MemberClassTypeHelper<M T::*>
68 using type = std::remove_cvref_t<T>;
71 template <
typename Record>
72 struct RecordTableNameImpl
74 static constexpr std::string_view Value = []() {
75 if constexpr (
requires { Record::TableName; })
76 return Record::TableName;
79#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
80 return std::meta::identifier_of(^^Record);
82 auto const typeName = Reflection::TypeNameOf<Record>;
83 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
84 return typeName.substr(i + 1);
94 template <
typename First,
typename Second>
95 struct RecordTableNameImpl<std::tuple<First, Second>>
97 static constexpr std::string_view Value = []() {
98 if constexpr (
requires { First::TableName; })
99 return First::TableName;
102#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
103 return std::meta::identifier_of(^^First);
105 auto const typeName = Reflection::TypeNameOf<First>;
106 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
107 return typeName.substr(i + 1);
114 template <
typename FieldType>
115 constexpr auto ColumnNameOverride = []()
consteval {
116 if constexpr (
requires { FieldType::ColumnNameOverride; })
117 return FieldType::ColumnNameOverride;
119 return std::string_view {};
121#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
122 template <auto reflection>
123 struct FieldNameOfImpl
125 using R =
typename[:std::meta::type_of(reflection):];
126 static constexpr std::string_view value = []()
constexpr -> std::string_view {
127 if constexpr (
requires { R::ColumnNameOverride; })
129 if constexpr (!R::ColumnNameOverride.empty())
130 return R::ColumnNameOverride;
132 return std::meta::identifier_of(reflection);
136 template <
typename ReferencedFieldType, auto F>
137 struct FieldNameOfImpl;
139 template <
typename T, auto F,
typename R>
140 struct FieldNameOfImpl<R T::*, F>
142 static constexpr std::string_view value = []()
constexpr -> std::string_view {
143 if constexpr (
requires { R::ColumnNameOverride; })
145 if constexpr (!R::ColumnNameOverride.empty())
146 return R::ColumnNameOverride;
148 return Reflection::NameOf<F>;
153 template <std::
size_t I,
typename Record>
156 using FieldType = Reflection::MemberTypeOf<I, Record>;
158 if constexpr (!std::string_view(ColumnNameOverride<FieldType>).empty())
160 return FieldType::ColumnNameOverride;
162 return Reflection::MemberNameOf<I, Record>;
169template <std::
size_t I,
typename Record>
170constexpr inline std::string_view
FieldNameAt = detail::FieldNameAt<I, Record>();
175template <
typename Record>
176constexpr std::string_view
RecordTableName = detail::RecordTableNameImpl<Record>::Value;
178template <
template <
typename...>
class S,
class T>
179concept IsSpecializationOf = detail::is_specialization_of<S, T>::value;
181#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
186template <std::meta::info ReflectionOfField>
187constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<ReflectionOfField>::value;
189template <auto Member>
190using MemberClassType =
typename[:std::meta::parent_of(Member):];
192template <auto Member>
193constexpr size_t MemberIndexOf = []()
consteval ->
size_t {
195 auto members = nonstatic_data_members_of(parent_of(Member), std::meta::access_context::current());
196 if (
auto it = std::ranges::find(members, Member); it != members.end())
198 index = std::distance(members.begin(), it);
199 return static_cast<size_t>(index);
208template <auto ReferencedField>
209constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<
decltype(ReferencedField), ReferencedField>::value;
211template <auto Member>
212constexpr size_t MemberIndexOf = Reflection::MemberIndexOf<Member>;
215using MemberClassType =
typename detail::MemberClassTypeHelper<T>::type;
221 template <auto ReferencedField>
222 struct FullyQualifiedNameOfImpl
224#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
225 static constexpr auto ClassName =
RecordTableName<
typename[:std::meta::parent_of(ReferencedField):]>;
227 static constexpr auto ClassName =
RecordTableName<MemberClassType<
decltype(ReferencedField)>>;
229 static constexpr auto FieldName = FieldNameOf<ReferencedField>;
230 static constexpr auto StorageSize = ClassName.size() + FieldName.size() + 6;
233 static constexpr auto Storage = []()
constexpr -> std::array<char, StorageSize> {
235 auto storage = std::array<char, StorageSize> {};
236 std::ranges::copy(
"\"", storage.begin());
237 std::ranges::copy(ClassName, storage.begin() + 1);
238 std::ranges::copy(
"\".\"", storage.begin() + 1 + ClassName.size());
239 std::ranges::copy(FieldName, storage.begin() + 1 + ClassName.size() + 3);
240 std::ranges::copy(
"\"", storage.begin() + 1 + ClassName.size() + 3 + FieldName.size());
241 storage.back() =
'\0';
245 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
249struct SqlRawColumnNameView
251 std::string_view value;
253 std::weak_ordering operator<=>(SqlRawColumnNameView
const& other)
const =
default;
255 [[nodiscard]]
constexpr auto begin() const noexcept
257 return value.begin();
260 [[nodiscard]]
constexpr auto end() const noexcept
265 [[nodiscard]]
constexpr auto size() const noexcept
270 [[nodiscard]]
constexpr auto empty() const noexcept
272 return value.empty();
275 [[nodiscard]]
constexpr auto data() const noexcept
280 [[nodiscard]]
constexpr std::string_view string_view() const noexcept
285 [[nodiscard]]
constexpr std::string to_string()
const
287 return std::string(value);
291constexpr bool operator==(SqlRawColumnNameView
const& lhs, std::string_view rhs)
noexcept
293 return lhs.value == rhs;
296constexpr bool operator!=(SqlRawColumnNameView
const& lhs, std::string_view rhs)
noexcept
298 return lhs.value != rhs;
310template <auto ReferencedField>
312 .value = detail::FullyQualifiedNameOfImpl<ReferencedField>::value,
317 template <
auto... ReferencedFields>
318 struct FullyQualifiedNamesOfImpl
320 static constexpr auto StorageSize =
321 1 + (2 * (
sizeof...(ReferencedFields) - 1)) + (0 + ... + FullyQualifiedNameOf<ReferencedFields>.size());
323 static constexpr std::array<char, StorageSize> Storage = []()
consteval {
324 auto result = std::array<char, StorageSize> {};
330 constexpr auto Delimiter = std::string_view(
", ");
331 std::ranges::copy(Delimiter, result.begin() + offset);
332 offset += Delimiter.size();
334 std::ranges::copy(FullyQualifiedNameOf<ReferencedFields>, result.begin() + offset);
335 offset += FullyQualifiedNameOf<ReferencedFields>.size();
338 result.back() =
'\0';
342 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
355template <
auto... ReferencedFields>
357 .value = detail::FullyQualifiedNamesOfImpl<ReferencedFields...>::value,
360LIGHTWEIGHT_API
void LogIfFailed(SQLHSTMT hStmt, SQLRETURN error, std::source_location sourceLocation);
362LIGHTWEIGHT_API
void RequireSuccess(SQLHSTMT hStmt,
364 std::source_location sourceLocation = std::source_location::current());
367enum class FormatType : uint8_t
380LIGHTWEIGHT_API std::string FormatName(std::string
const& name, FormatType formatType);
383LIGHTWEIGHT_API std::string FormatName(std::string_view name, FormatType formatType);
390 [[nodiscard]] LIGHTWEIGHT_API
bool IsColliding(std::string
const& name)
const noexcept;
393 [[nodiscard]] LIGHTWEIGHT_API std::optional<std::string>
TryDeclareName(std::string name);
396 [[nodiscard]] LIGHTWEIGHT_API std::string
DeclareName(std::string name);
399 std::unordered_map<std::string, size_t> _collisionMap;
Maintains collisions to create unique names.
LIGHTWEIGHT_API std::string DeclareName(std::string name)
Creates a name that is definitely not colliding.
LIGHTWEIGHT_API bool IsColliding(std::string const &name) const noexcept
Tests if the given name is already registered.
LIGHTWEIGHT_API std::optional< std::string > TryDeclareName(std::string name)
Tries to declare a name and returns it, otherwise returns std::nullopt.
constexpr std::string_view RecordTableName
Holds the SQL tabl ename for the given record type.
constexpr std::string_view FieldNameAt
Returns the SQL field name of the given field index in the record.
constexpr auto FullyQualifiedNamesOf
Holds the quoted fully qualified field names of the given fields.
constexpr auto FullyQualifiedNameOf
Holds the quoted fully qualified field name (including table name) of the given field.