Messagelib

searchlinecommand.cpp
1/*
2 SPDX-FileCopyrightText: 2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "searchlinecommand.h"
8#include <KLocalizedString>
9#include <QDateTime>
10
11// #define DEBUG_COMMAND_PARSER 1
12
13using namespace Qt::Literals::StringLiterals;
14using namespace MessageList::Core;
15QMap<QString, SearchLineCommand::SearchLineType> SearchLineCommand::mKeyList = {
16 {"subject"_L1, SearchLineCommand::SearchLineType::Subject},
17 {"body"_L1, SearchLineCommand::SearchLineType::Body},
18 {"to"_L1, SearchLineCommand::SearchLineType::To},
19 {"cc"_L1, SearchLineCommand::SearchLineType::Cc},
20 {"bcc"_L1, SearchLineCommand::SearchLineType::Bcc},
21 {"from"_L1, SearchLineCommand::SearchLineType::From},
22 {"has:attachment"_L1, SearchLineCommand::SearchLineType::HasAttachment},
23 {"has:invitation"_L1, SearchLineCommand::SearchLineType::HasInvitation},
24 {"is:unread"_L1, SearchLineCommand::SearchLineType::IsUnRead},
25 {"is:read"_L1, SearchLineCommand::SearchLineType::IsRead},
26 {"is:important"_L1, SearchLineCommand::SearchLineType::IsImportant},
27 {"is:ignored"_L1, SearchLineCommand::SearchLineType::IsIgnored},
28 {"is:ham"_L1, SearchLineCommand::SearchLineType::IsHam},
29 {"is:spam"_L1, SearchLineCommand::SearchLineType::IsSpam},
30 {"is:watched"_L1, SearchLineCommand::SearchLineType::IsWatched},
31 {"is:replied"_L1, SearchLineCommand::SearchLineType::IsReplied},
32 {"is:forwarded"_L1, SearchLineCommand::SearchLineType::IsForwarded},
33 {"size"_L1, SearchLineCommand::SearchLineType::Size},
34 {"smaller"_L1, SearchLineCommand::SearchLineType::Larger},
35 {"larger"_L1, SearchLineCommand::SearchLineType::Smaller},
36 {"older_than"_L1, SearchLineCommand::SearchLineType::OlderThan},
37 {"newer_than"_L1, SearchLineCommand::SearchLineType::NewerThan},
38 {"category"_L1, SearchLineCommand::SearchLineType::Category},
39 // after:before:older:newer:
40 // category:
41 // TODO add support for OR
42};
43SearchLineCommand::SearchLineCommand() = default;
44
45SearchLineCommand::~SearchLineCommand() = default;
46
47bool SearchLineCommand::hasSubType(const QString &v)
48{
49 return v == QLatin1StringView("is") || v == QLatin1StringView("has");
50}
51
52bool SearchLineCommand::hasSubType(SearchLineCommand::SearchLineType type)
53{
54 return type == Date || type == Size || type == To || type == Bcc || type == Cc || type == From || type == Subject || type == Smaller || type == Larger
55 || type == OlderThan || type == NewerThan || type == Body || type == Category;
56}
57
58bool SearchLineCommand::isEmpty() const
59{
60 return mSearchLineInfo.isEmpty();
61}
62
63QString SearchLineCommand::convertSearchLinetypeToTranslatedString(SearchLineCommand::SearchLineType type) const
64{
65 switch (type) {
66 case Unknown:
67 case HasStateOrAttachment:
68 return {};
69 // TODO implement Date
70 case Date:
71 case OlderThan:
72 case NewerThan:
73 return {};
74 case Literal:
75 return i18n("Literal string");
76 case Smaller:
77 return i18n("Size is smaller than");
78 case Larger:
79 return i18n("Size is larger than");
80 case Size:
81 return i18n("Size is");
82 case To:
83 return i18n("To contains");
84 case Bcc:
85 return i18n("BCC contains");
86 case Cc:
87 return i18n("CC contains");
88 case From:
89 return i18n("From contains");
90 case Subject:
91 return i18n("Subject contains");
92 case Body:
93 return i18n("Body contains");
94 case Category:
95 return i18n("Mail has tag");
96 case HasAttachment:
97 return i18n("Mail has attachment");
98 case HasInvitation:
99 return i18n("Mail has invitation");
100 case IsImportant:
101 return i18n("Mail is important");
102 case IsRead:
103 return i18n("Mail is read");
104 case IsUnRead:
105 return i18n("Mail is Unread");
106 case IsIgnored:
107 return i18n("Mail is Ignored");
108 case IsHam:
109 return i18n("Mail is Ham");
110 case IsSpam:
111 return i18n("Mail is Spam");
112 case IsWatched:
113 return i18n("Mail is watched");
114 case IsReplied:
115 return i18n("Mail is replied");
116 case IsForwarded:
117 return i18n("Mail is forwarded");
118 }
119 return {};
120}
121
122QString SearchLineCommand::generateCommadLineStr() const
123{
124 QString result;
125 for (const auto &info : mSearchLineInfo) {
126 if (!result.isEmpty()) {
127 result += QLatin1Char(' ') + i18n("AND") + QLatin1Char(' ');
128 }
129 const QString translatedType = convertSearchLinetypeToTranslatedString(info.type);
130 if (!translatedType.isEmpty()) {
131 result += translatedType;
132 }
133 if (!info.argument.isEmpty()) {
134 if (!translatedType.isEmpty()) {
135 result += QLatin1Char(' ');
136 }
137 result += info.argument;
138 }
139 }
140 return result;
141}
142
143SearchLineCommand::SearchLineInfo SearchLineCommand::isAnotherInfo(QString tmp, SearchLineInfo searchLineInfo)
144{
145 if (!tmp.contains(QLatin1Char(' '))) {
146 return {};
147 }
148 if (tmp.endsWith(QLatin1StringView("is")) || tmp.endsWith(QLatin1StringView("has"))) {
149#ifdef DEBUG_COMMAND_PARSER
150 qDebug() << " found has subtype " << tmp;
151#endif
152 return {};
153 }
154 const QStringList keys = mKeyList.keys();
155 for (const QString &key : keys) {
156 if (tmp.endsWith(key)) {
157#ifdef DEBUG_COMMAND_PARSER
158 qDebug() << " found element !!!!!! " << tmp;
159#endif
160 tmp.remove(key);
161 tmp.removeLast(); // Remove last space
162 searchLineInfo.argument = tmp;
163 if (!searchLineInfo.argument.isEmpty() && searchLineInfo.type == Unknown) {
164 searchLineInfo.type = Literal;
165 }
166 // qDebug() << " AAAAAAAAAAAAAAAAAAAAAAAAAAA " << searchLineInfo;
167 if (searchLineInfo.isValid()) {
168 appendSearchLineInfo(searchLineInfo);
169 }
170#ifdef DEBUG_COMMAND_PARSER
171 qDebug() << " Add searchLineInfo" << searchLineInfo << mSearchLineInfo;
172#endif
173
174 SearchLineInfo info;
175 info.type = mKeyList.value(key);
176 return info;
177 }
178 }
179 return {};
180}
181
182void SearchLineCommand::parseSearchLineCommand(const QString &str)
183{
184 mSearchLineInfo.clear();
185 if (str.isEmpty()) {
186 return;
187 }
188 SearchLineInfo searchLineInfo;
189 QString tmp;
190 int parentheses = 0;
191 for (int i = 0, total = str.length(); i < total; ++i) {
192 const QChar ch = str.at(i);
193 if (ch == QLatin1Char(':')) {
194#ifdef DEBUG_COMMAND_PARSER
195 qDebug() << " tmp ! " << tmp;
196#endif
197 const SearchLineCommand::SearchLineInfo newInfo = isAnotherInfo(tmp, searchLineInfo);
198 if (newInfo.type != Unknown) {
199 tmp.clear();
200 searchLineInfo = newInfo;
201 // qDebug() << " vxvxcvxcvxcv " << tmp;
202 } else if (mKeyList.contains(tmp.trimmed())) {
203#ifdef DEBUG_COMMAND_PARSER
204 qDebug() << " contains " << tmp;
205#endif
206 searchLineInfo.type = mKeyList.value(tmp.trimmed());
207 tmp.clear();
208 } else if (hasSubType(tmp)) {
209 searchLineInfo.type = HasStateOrAttachment;
210 tmp += ch;
211 // continue
212 } else {
213 tmp += ch;
214 }
215 } else if (ch.isSpace()) {
216 const SearchLineCommand::SearchLineInfo newInfo = isAnotherInfo(tmp, searchLineInfo);
217 if (newInfo.type != Unknown) {
218 tmp.clear();
219 searchLineInfo = newInfo;
220 // qDebug() << " vxvxcvxcvxcv " << tmp;
221 } else if (mKeyList.contains(tmp)) { // We can use is:... or has:...
222 searchLineInfo.type = mKeyList.value(tmp);
223 tmp.clear();
224 }
225#ifdef DEBUG_COMMAND_PARSER
226 qDebug() << " is space " << "pare" << parentheses << " tmp " << tmp << "searchLineInfo.type " << searchLineInfo.type
227 << " searchLineInfo.argument.isEmpty() " << searchLineInfo.argument.isEmpty();
228#endif
229 if (tmp.isEmpty() && hasSubType(searchLineInfo.type) && parentheses == 0) {
230#ifdef DEBUG_COMMAND_PARSER
231 qDebug() << "clear invalid type" << searchLineInfo;
232#endif
233 searchLineInfo.type = Unknown;
234 tmp.clear();
235 } else if (hasSubType(searchLineInfo.type)) {
236 tmp += ch;
237 } else if (searchLineInfo.type != Unknown && parentheses == 0) {
238 searchLineInfo.argument = tmp;
239 tmp.clear();
240#ifdef DEBUG_COMMAND_PARSER
241 qDebug() << "clear tmp argument " << searchLineInfo;
242#endif
243 } else { // Literal
244 tmp += ch;
245 }
246 if (searchLineInfo.isValid() && parentheses == 0) {
247 appendSearchLineInfo(searchLineInfo);
248 searchLineInfo.clear();
249 tmp.clear();
250 }
251 } else if (ch == QLatin1Char('(')) {
252 parentheses++;
253 if (parentheses > 1) {
254 tmp += ch;
255 }
256#ifdef DEBUG_COMMAND_PARSER
257 qDebug() << " parenthese ( equal " << parentheses;
258#endif
259 } else if (ch == QLatin1Char(')')) {
260 parentheses--;
261 if (parentheses > 0) {
262 tmp += ch;
263 }
264#ifdef DEBUG_COMMAND_PARSER
265 qDebug() << " parenthese ) equal " << parentheses;
266#endif
267 if (parentheses == 0) {
268 searchLineInfo.argument = tmp;
269 tmp.clear();
270#ifdef DEBUG_COMMAND_PARSER
271 qDebug() << " new values " << searchLineInfo;
272#endif
273 appendSearchLineInfo(searchLineInfo);
274 searchLineInfo.clear();
275 }
276 } else {
277 tmp += ch;
278#ifdef DEBUG_COMMAND_PARSER
279 qDebug() << " tmp " << tmp << " ch " << ch << "end";
280#endif
281 }
282 }
283 if (searchLineInfo.type != Unknown) {
284 if (searchLineInfo.type == HasStateOrAttachment) {
285#ifdef DEBUG_COMMAND_PARSER
286 qDebug() << " type is HasStateOrAttachment";
287#endif
288 if (mKeyList.contains(tmp)) {
289 searchLineInfo.type = mKeyList.value(tmp);
290 appendSearchLineInfo(searchLineInfo);
291 }
292 } else {
293 if (!tmp.isEmpty()) {
294#ifdef DEBUG_COMMAND_PARSER
295 qDebug() << " add as original searchLineInfo" << searchLineInfo;
296#endif
297 const SearchLineCommand::SearchLineInfo newInfo = isAnotherInfo(tmp, searchLineInfo);
298 if (newInfo.type != Unknown) {
299 searchLineInfo = newInfo;
300 } else {
301 searchLineInfo.argument = tmp;
302 }
303 appendSearchLineInfo(searchLineInfo);
304 }
305 }
306 } else {
307 if (!tmp.isEmpty()) {
308 searchLineInfo.type = Literal;
309 searchLineInfo.argument = tmp;
310 appendSearchLineInfo(searchLineInfo);
311 }
312 }
313#ifdef DEBUG_COMMAND_PARSER
314 qDebug() << " END " << mSearchLineInfo;
315#endif
316 // TODO add date ?
317 // TODO add support for double quote
318 // We need to extend emailquery or creating query by hand
319}
320
321void SearchLineCommand::appendSearchLineInfo(SearchLineInfo searchLineInfo)
322{
323 if (searchLineInfo.mustBeUnique()) {
324 if (mSearchLineInfo.contains(searchLineInfo)) {
325#ifdef DEBUG_COMMAND_PARSER
326 qDebug() << " Already exist " << searchLineInfo;
327#endif
328 return;
329 }
330 }
331 mSearchLineInfo.append(std::move(searchLineInfo));
332}
333
334QList<SearchLineCommand::SearchLineInfo> SearchLineCommand::searchLineInfo() const
335{
336 return mSearchLineInfo;
337}
338
339void SearchLineCommand::setSearchLineInfo(const QList<SearchLineInfo> &newSearchLineInfo)
340{
341 mSearchLineInfo = newSearchLineInfo;
342}
343
344void SearchLineCommand::SearchLineInfo::clear()
345{
346 type = SearchLineCommand::SearchLineType::Unknown;
347 argument.clear();
348}
349
350bool SearchLineCommand::SearchLineInfo::isValid() const
351{
352 if (type == SearchLineType::Unknown || type == SearchLineCommand::HasStateOrAttachment) {
353 return false;
354 }
355 if (type == SearchLineType::Literal && !argument.isEmpty()) {
356 return true;
357 }
358 if (hasSubType(type) && !argument.isEmpty()) {
359 return true;
360 }
361 if (!hasSubType(type) && argument.isEmpty()) {
362 return true;
363 }
364 return false;
365}
366
367bool SearchLineCommand::SearchLineInfo::operator==(const SearchLineInfo &other) const
368{
369 return type == other.type && argument == other.argument;
370}
371
372bool SearchLineCommand::SearchLineInfo::isValidDate() const
373{
374 if (argument.isEmpty()) {
375 return false;
376 }
377 return QDateTime::fromString(argument).isValid();
378}
379
380bool SearchLineCommand::SearchLineInfo::mustBeUnique() const
381{
382 return type == HasAttachment || type == IsImportant || type == IsRead || type == IsUnRead || type == IsIgnored || type == IsHam || type == IsSpam
383 || type == IsWatched || type == IsReplied || type == IsForwarded;
384}
385
386qint64 SearchLineCommand::SearchLineInfo::convertArgumentAsSize() const
387{
388 // TODO convert it
389 return {};
390}
391
392QDebug operator<<(QDebug d, const MessageList::Core::SearchLineCommand::SearchLineInfo &info)
393{
394 d << " type " << info.type;
395 d << " argument " << info.argument;
396 return d;
397}
398
399#include "moc_searchlinecommand.cpp"
QString i18n(const char *text, const TYPE &arg...)
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
The implementation independent part of the MessageList library.
Definition aggregation.h:22
bool isSpace(char32_t ucs4)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
bool isEmpty() const const
bool contains(const Key &key) const const
QList< Key > keys() const const
T value(const Key &key, const T &defaultValue) const const
QString & append(QChar ch)
const QChar at(qsizetype position) const const
void clear()
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & removeLast()
QString trimmed() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:33:26 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.