Lightweight 0.20250627.0
Loading...
Searching...
No Matches
Record.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../Utils.hpp"
6#include "Field.hpp"
7
8#include <reflection-cpp/reflection.hpp>
9
10#include <concepts>
11#include <limits>
12
13/// @brief Represents a sequence of indexes that can be used alongside Query() to retrieve only part of the record.
14///
15/// @ingroup DataMapper
16template <size_t... Ints>
17using SqlElements = std::integer_sequence<size_t, Ints...>;
18
19namespace detail
20{
21// Helper trait to detect specializations of SqlElements
22template <typename T>
23struct IsSqlElements: std::false_type
24{
25};
26
27template <size_t... Ints>
28struct IsSqlElements<SqlElements<Ints...>>: std::true_type
29{
30};
31} // namespace detail
32
33// @brief Helper concept to check if a type is not a specialization of SqlElements
34template <typename T>
35concept NotSqlElements = !detail::IsSqlElements<T>::value;
36
37/// @brief Represents a record type that can be used with the DataMapper.
38///
39/// The record type must be an aggregate type.
40///
41/// @see DataMapper, Field, BelongsTo, HasMany, HasManyThrough, HasOneThrough
42/// @ingroup DataMapper
43template <typename Record>
44concept DataMapperRecord = std::is_aggregate_v<Record> && NotSqlElements<Record>;
45
46namespace detail
47{
48
49template <std::size_t I, typename Record>
50constexpr std::optional<size_t> FindPrimaryKeyIndex()
51{
52 static_assert(DataMapperRecord<Record>, "Record must satisfy DataMapperRecord");
53 if constexpr (I < Reflection::CountMembers<Record>)
54 {
55 if constexpr (IsPrimaryKey<Reflection::MemberTypeOf<I, Record>>)
56 return { I };
57 else
58 return FindPrimaryKeyIndex<I + 1, Record>();
59 }
60 return std::nullopt;
61}
62
63} // namespace detail
64
65/// Declare RecordPrimaryKeyIndex<Record> to retrieve the primary key index of the given record.
66template <typename Record>
67constexpr size_t RecordPrimaryKeyIndex =
68 detail::FindPrimaryKeyIndex<0, Record>().value_or((std::numeric_limits<size_t>::max)());
69
70/// Retrieves a reference to the given record's primary key.
71template <typename Record>
72decltype(auto) RecordPrimaryKeyOf(Record&& record)
73{
74 // static_assert(DataMapperRecord<Record>, "Record must satisfy DataMapperRecord");
75 // static_assert(RecordPrimaryKeyIndex<Record> != static_cast<size_t>(-1), "Record must have a primary key");
76 return Reflection::GetMemberAt<RecordPrimaryKeyIndex<std::remove_cvref_t<Record>>>(std::forward<Record>(record));
77}
78
79namespace details
80{
81
82template <typename Record>
83struct RecordPrimaryKeyTypeHelper
84{
85 using type = void;
86};
87
88template <typename Record>
89 requires(RecordPrimaryKeyIndex<Record> < Reflection::CountMembers<Record>)
90struct RecordPrimaryKeyTypeHelper<Record>
91{
92 using type = typename Reflection::MemberTypeOf<RecordPrimaryKeyIndex<Record>, Record>::ValueType;
93};
94
95} // namespace details
96
97/// Reflects the primary key type of the given record.
98template <typename Record>
99using RecordPrimaryKeyType = typename details::RecordPrimaryKeyTypeHelper<Record>::type;
100
101/// @brief Maps the fields of the given record to the target that supports the operator[].
102template <typename Record, typename TargetMappable>
103void MapFromRecordFields(Record&& record, TargetMappable& target)
104{
105 Reflection::EnumerateMembers(std::forward<Record>(record), [&]<std::size_t I>(auto const& field) {
106 using MemberType = Reflection::MemberTypeOf<I, Record>;
107 static_assert(IsField<MemberType>, "Record member must be a Field<> type");
108 static_assert(std::is_assignable_v<decltype(target[I]), decltype(field.Value())>,
109 "Target must support operator[] with the field type");
110 target[I] = field.Value();
111 });
112}
Represents a record type that can be used with the DataMapper.
Definition Record.hpp:44
std::integer_sequence< size_t, Ints... > SqlElements
Represents a sequence of indexes that can be used alongside Query() to retrieve only part of the reco...
Definition Record.hpp:17