QXmpp Version: 1.15.1
Loading...
Searching...
No Matches
Enums.h
1// SPDX-FileCopyrightText: 2025 Linus Jahn <lnj@kaidan.im>
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4
5#ifndef ENUMS_H
6#define ENUMS_H
7
8#include "StringLiterals.h"
9
10#include <array>
11#include <optional>
12#include <ranges>
13
14#include <QDebug>
15#include <QList>
16#include <QString>
17
18namespace QXmpp::Private::Enums {
19
20// std::array helper
21namespace detail {
22
23template<class T, std::size_t N, std::size_t... I>
24constexpr std::array<std::remove_cv_t<T>, N> to_array_impl(T (&&a)[N], std::index_sequence<I...>)
25{
26 return { { std::move(a[I])... } };
27}
28
29} // namespace detail
30
31template<typename Enum, std::size_t N>
32consteval std::array<std::remove_cv_t<std::tuple<Enum, QStringView>>, N> makeValues(std::tuple<Enum, QStringView> (&&a)[N])
33{
34 return detail::to_array_impl(std::move(a), std::make_index_sequence<N> {});
35}
36
37template<typename Enum>
38struct Data;
39
40// order check
41template<typename Enum, size_t N>
42consteval bool checkEnumOrder(const std::array<std::tuple<Enum, QStringView>, N> &values)
43{
44 size_t offset = 0;
45 if constexpr (N > 0) {
46 offset = size_t(std::get<0>(values[0]));
47 }
48 for (size_t i = offset; i < (N + offset); ++i) {
49 if (i != size_t(std::get<0>(values[i]))) {
50 return false;
51 }
52 }
53 return true;
54}
55
56template<typename Enum>
57concept SerializableEnum = requires {
58 typename Data<Enum>;
59
60 requires std::ranges::random_access_range<decltype(Data<Enum>::Values)>;
61 requires std::same_as<
62 std::ranges::range_value_t<decltype(Data<Enum>::Values)>,
63 std::tuple<Enum, QStringView>>;
64 requires checkEnumOrder<Enum>(Data<Enum>::Values);
65};
66
67template<typename Enum>
68concept SerializableFlags = requires {
69 typename Data<Enum>;
70 requires Data<Enum>::IsFlags == true;
71
72 requires std::ranges::random_access_range<decltype(Data<Enum>::Values)>;
73 requires std::same_as<
74 std::ranges::range_value_t<decltype(Data<Enum>::Values)>,
75 std::tuple<Enum, QStringView>>;
76};
77
78template<typename Enum>
79concept NullableEnum = requires(Enum value) {
80 typename Data<Enum>;
81 { Data<Enum>::NullValue } -> std::convertible_to<Enum>;
82};
83
84template<typename Enum>
85constexpr std::optional<Enum> fromString(QStringView str)
86 requires SerializableEnum<Enum> || SerializableFlags<Enum>
87{
88 constexpr auto values = Data<Enum>::Values;
89
90 auto enumPart = [](const auto &v) { return std::get<0>(v); };
91 auto stringPart = [](const auto &v) { return std::get<1>(v); };
92
93 if (auto itr = std::ranges::find(values, str, stringPart); itr != values.end()) {
94 return enumPart(*itr);
95 }
96 return {};
97}
98
99template<SerializableEnum Enum>
100constexpr QStringView toStringView(Enum value)
101{
102 // offset for enums that do not start at 0
103 constexpr auto offset = size_t(std::get<0>(Data<Enum>::Values[0]));
104
105 auto &[enumerator, string] = Data<Enum>::Values.at(size_t(value) - offset);
106 return string;
107}
108
109template<SerializableEnum Enum>
110inline QString toString(Enum value)
111{
112 return staticString(toStringView(value));
113}
114
115template<SerializableFlags Enum, typename Container>
116constexpr QFlags<Enum> fromStrings(const Container &container)
117{
118 QFlags<Enum> result;
119 for (const auto &string : container) {
120 if (auto flag = fromString<Enum>(string)) {
121 result.setFlag(*flag);
122 }
123 }
124 return result;
125}
126
127template<SerializableFlags Enum>
128constexpr auto toStrings(QFlags<Enum> value)
129{
130 auto testFlag = [value](auto entry) {
131 auto &[enumerator, string] = entry;
132 return value.testFlag(enumerator);
133 };
134 auto string = [](auto entry) { return std::get<1>(entry); };
135
136 return std::views::filter(Data<Enum>::Values, testFlag) | std::views::transform(string);
137}
138
139template<SerializableFlags Enum>
140QList<QString> toStringList(QFlags<Enum> value)
141{
142 auto strings = toStrings(value) | std::views::transform([](QStringView sv) { return staticString(sv); });
143 return QList<QString>(strings.begin(), strings.end());
144}
145
146} // namespace QXmpp::Private::Enums
147
148// operators
149
150template<QXmpp::Private::Enums::SerializableEnum Enum>
151bool operator==(QStringView s, Enum e) { return s == QXmpp::Private::Enums::toStringView(e); }
152template<QXmpp::Private::Enums::SerializableEnum Enum>
153bool operator!=(QStringView s, Enum e) { return s != QXmpp::Private::Enums::toStringView(e); }
154
155template<QXmpp::Private::Enums::SerializableEnum Enum>
156struct QConcatenable<Enum> {
157 typedef Enum type;
158 typedef QString ConvertTo;
159 enum { ExactSize = true };
160
161 static int size(Enum e)
162 {
163 return QXmpp::Private::Enums::toStringView(e).size();
164 }
165 static void appendTo(Enum m, QChar *&out)
166 {
167 QConcatenable<QStringView>::appendTo(QXmpp::Private::Enums::toStringView(m), out);
168 }
169};
170
171template<QXmpp::Private::Enums::SerializableEnum Enum>
172QDebug operator<<(QDebug debug, Enum e)
173{
174 return debug << QXmpp::Private::Enums::toStringView(e);
175}
176
177#endif // ENUMS_H