data/ServiceCatalog.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "ServiceCatalog.h" | ||
| 2 | |||
| 3 | #include <QFile> | ||
| 4 | #include <QJsonArray> | ||
| 5 | #include <QJsonDocument> | ||
| 6 | #include <QJsonObject> | ||
| 7 | #include <QLocale> | ||
| 8 | #include <QLoggingCategory> | ||
| 9 | |||
| 10 | ✗ | Q_LOGGING_CATEGORY(lcServiceCatalog, "mailjd.contacts.social") | |
| 11 | |||
| 12 | namespace { | ||
| 13 | |||
| 14 | 494 | QString fill(QString text, const QRegularExpressionMatch &match) { | |
| 15 |
2/2✓ Branch 19 → 4 taken 1976 times.
✓ Branch 19 → 20 taken 494 times.
|
2470 | for (const char *group : {"user", "server", "id", "url"}) |
| 16 |
3/3✓ Branch 6 → 7 taken 1976 times.
✓ Branch 12 → 13 taken 1976 times.
✓ Branch 13 → 14 taken 1976 times.
|
5928 | text.replace(QStringLiteral("{%1}").arg(QLatin1String(group)), match.captured(QLatin1String(group))); |
| 17 | 494 | return text; | |
| 18 | } | ||
| 19 | |||
| 20 | // "https://anasilva.example/" → "anasilva.example". | ||
| 21 | 2 | QString shortLink(QString url) { | |
| 22 |
2/2✓ Branch 13 → 4 taken 4 times.
✓ Branch 13 → 14 taken 2 times.
|
6 | for (const char *scheme : {"https://", "http://"}) |
| 23 |
3/3✓ Branch 5 → 6 taken 4 times.
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 12 taken 2 times.
|
4 | if (url.startsWith(QLatin1String(scheme), Qt::CaseInsensitive)) |
| 24 |
1/1✓ Branch 8 → 9 taken 2 times.
|
2 | url = url.mid(int(qstrlen(scheme))); |
| 25 |
3/3✓ Branch 15 → 16 taken 2 times.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 21 taken 1 time.
|
2 | if (url.startsWith(QLatin1String("www."))) |
| 26 |
1/1✓ Branch 17 → 18 taken 1 time.
|
1 | url = url.mid(4); |
| 27 |
3/3✓ Branch 25 → 26 taken 4 times.
✓ Branch 26 → 22 taken 2 times.
✓ Branch 26 → 27 taken 2 times.
|
4 | while (url.endsWith(QLatin1Char('/'))) |
| 28 | 2 | url.chop(1); | |
| 29 | 2 | return url; | |
| 30 | } | ||
| 31 | |||
| 32 | } // namespace | ||
| 33 | |||
| 34 | 1010 | const ServiceCatalog &ServiceCatalog::builtIn() { | |
| 35 | 32 | static const ServiceCatalog catalog = [] { | |
| 36 |
1/1✓ Branch 5 → 6 taken 16 times.
|
16 | QFile file(QStringLiteral(":/contacts/services.json")); |
| 37 |
2/3✓ Branch 9 → 10 taken 16 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 22 taken 16 times.
|
16 | if (!file.open(QIODevice::ReadOnly)) { |
| 38 | ✗ | qCWarning(lcServiceCatalog) << "Service catalog resource missing"; | |
| 39 | ✗ | return ServiceCatalog{}; | |
| 40 | } | ||
| 41 | 16 | QString error; | |
| 42 |
2/2✓ Branch 22 → 23 taken 16 times.
✓ Branch 23 → 24 taken 16 times.
|
16 | ServiceCatalog loaded = fromJson(file.readAll(), &error); |
| 43 |
1/2✗ Branch 26 → 27 not taken.
✓ Branch 26 → 39 taken 16 times.
|
16 | if (!error.isEmpty()) |
| 44 | ✗ | qCWarning(lcServiceCatalog) << "Service catalog:" << error; | |
| 45 | 16 | return loaded; | |
| 46 |
4/7✓ Branch 2 → 3 taken 16 times.
✓ Branch 2 → 9 taken 994 times.
✓ Branch 4 → 5 taken 16 times.
✗ Branch 4 → 9 not taken.
✓ Branch 5 → 6 taken 16 times.
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 14 not taken.
|
1026 | }(); |
| 47 | 1010 | return catalog; | |
| 48 | } | ||
| 49 | |||
| 50 | 18 | ServiceCatalog ServiceCatalog::fromJson(const QByteArray &json, QString *error) { | |
| 51 | 18 | ServiceCatalog catalog; | |
| 52 | 18 | QJsonParseError parseError; | |
| 53 |
1/1✓ Branch 2 → 3 taken 18 times.
|
18 | const QJsonDocument document = QJsonDocument::fromJson(json, &parseError); |
| 54 |
3/3✓ Branch 3 → 4 taken 18 times.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 11 taken 17 times.
|
18 | if (!document.isObject()) { |
| 55 |
1/2✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 10 not taken.
|
1 | if (error) |
| 56 |
1/1✓ Branch 6 → 7 taken 1 time.
|
1 | *error = parseError.errorString(); |
| 57 | 1 | return catalog; | |
| 58 | } | ||
| 59 |
8/8✓ Branch 11 → 12 taken 17 times.
✓ Branch 15 → 16 taken 17 times.
✓ Branch 16 → 17 taken 17 times.
✓ Branch 21 → 22 taken 17 times.
✓ Branch 22 → 23 taken 17 times.
✓ Branch 25 → 26 taken 289 times.
✓ Branch 235 → 24 taken 289 times.
✓ Branch 235 → 236 taken 17 times.
|
323 | for (const QJsonValue &item : document.object().value(QStringLiteral("services")).toArray()) { |
| 60 |
1/1✓ Branch 26 → 27 taken 289 times.
|
289 | const QJsonObject object = item.toObject(); |
| 61 | 289 | Service service; | |
| 62 |
2/2✓ Branch 30 → 31 taken 289 times.
✓ Branch 31 → 32 taken 289 times.
|
289 | service.id = object.value(QStringLiteral("id")).toString(); |
| 63 |
2/2✓ Branch 40 → 41 taken 289 times.
✓ Branch 41 → 42 taken 289 times.
|
289 | service.kind = object.value(QStringLiteral("kind")).toString(); |
| 64 |
2/2✓ Branch 50 → 51 taken 289 times.
✓ Branch 51 → 52 taken 289 times.
|
289 | service.glyph = object.value(QStringLiteral("glyph")).toString(); |
| 65 |
3/3✓ Branch 60 → 61 taken 289 times.
✓ Branch 61 → 62 taken 289 times.
✓ Branch 62 → 63 taken 289 times.
|
289 | service.codepoint = object.value(QStringLiteral("codepoint")).toString().toUInt(nullptr, 16); |
| 66 |
4/4✓ Branch 70 → 71 taken 289 times.
✓ Branch 71 → 72 taken 289 times.
✓ Branch 75 → 76 taken 289 times.
✓ Branch 76 → 77 taken 289 times.
|
578 | service.nameEn = object.value(QStringLiteral("names")).toObject().value(QStringLiteral("en")).toString(); |
| 67 |
4/4✓ Branch 89 → 90 taken 289 times.
✓ Branch 90 → 91 taken 289 times.
✓ Branch 94 → 95 taken 289 times.
✓ Branch 95 → 96 taken 289 times.
|
578 | service.nameDe = object.value(QStringLiteral("names")).toObject().value(QStringLiteral("de")).toString(); |
| 68 |
7/7✓ Branch 108 → 109 taken 289 times.
✓ Branch 109 → 110 taken 289 times.
✓ Branch 113 → 114 taken 289 times.
✓ Branch 114 → 115 taken 289 times.
✓ Branch 117 → 118 taken 128 times.
✓ Branch 124 → 116 taken 128 times.
✓ Branch 124 → 125 taken 289 times.
|
417 | for (const QJsonValue &alias : object.value(QStringLiteral("aliases")).toArray()) |
| 69 |
2/2✓ Branch 118 → 119 taken 128 times.
✓ Branch 119 → 120 taken 128 times.
|
417 | service.aliases.append(alias.toString()); |
| 70 |
7/7✓ Branch 129 → 130 taken 289 times.
✓ Branch 130 → 131 taken 289 times.
✓ Branch 134 → 135 taken 289 times.
✓ Branch 135 → 136 taken 289 times.
✓ Branch 138 → 139 taken 433 times.
✓ Branch 195 → 137 taken 433 times.
✓ Branch 195 → 196 taken 289 times.
|
722 | for (const QJsonValue &pattern : object.value(QStringLiteral("patterns")).toArray()) { |
| 71 |
1/1✓ Branch 139 → 140 taken 433 times.
|
433 | Service::Pattern entry; |
| 72 |
3/3✓ Branch 141 → 142 taken 433 times.
✓ Branch 145 → 146 taken 433 times.
✓ Branch 146 → 147 taken 433 times.
|
1299 | entry.match = QRegularExpression(pattern.toObject().value(QStringLiteral("match")).toString(), |
| 73 |
1/1✓ Branch 147 → 148 taken 433 times.
|
866 | QRegularExpression::CaseInsensitiveOption); |
| 74 |
3/3✓ Branch 155 → 156 taken 433 times.
✓ Branch 159 → 160 taken 433 times.
✓ Branch 160 → 161 taken 433 times.
|
866 | entry.requiredKind = pattern.toObject().value(QStringLiteral("kind")).toString(); |
| 75 |
3/3✓ Branch 167 → 168 taken 433 times.
✓ Branch 168 → 169 taken 1 time.
✓ Branch 168 → 182 taken 432 times.
|
433 | if (!entry.match.isValid()) { |
| 76 |
1/2✓ Branch 169 → 170 taken 1 time.
✗ Branch 169 → 181 not taken.
|
1 | if (error) |
| 77 |
2/2✓ Branch 173 → 174 taken 1 time.
✓ Branch 174 → 175 taken 1 time.
|
1 | *error = QStringLiteral("Invalid pattern in %1: %2").arg(service.id, entry.match.errorString()); |
| 78 | 1 | continue; | |
| 79 | } | ||
| 80 |
1/1✓ Branch 182 → 183 taken 432 times.
|
432 | service.patterns.append(entry); |
| 81 | 723 | } | |
| 82 |
2/2✓ Branch 200 → 201 taken 289 times.
✓ Branch 201 → 202 taken 289 times.
|
289 | service.handleTemplate = object.value(QStringLiteral("handle")).toString(); |
| 83 |
2/2✓ Branch 210 → 211 taken 289 times.
✓ Branch 211 → 212 taken 289 times.
|
289 | service.urlTemplate = object.value(QStringLiteral("url")).toString(); |
| 84 |
2/2✓ Branch 220 → 221 taken 289 times.
✓ Branch 221 → 222 taken 289 times.
|
289 | service.uriTemplate = object.value(QStringLiteral("uri")).toString(); |
| 85 |
1/2✓ Branch 228 → 229 taken 289 times.
✗ Branch 228 → 230 not taken.
|
289 | if (!service.id.isEmpty()) |
| 86 |
1/1✓ Branch 229 → 230 taken 289 times.
|
289 | catalog.m_services.append(service); |
| 87 | 306 | } | |
| 88 | 17 | return catalog; | |
| 89 | 18 | } | |
| 90 | |||
| 91 | 1862 | const ServiceCatalog::Service *ServiceCatalog::find(const QString &id) const { | |
| 92 |
2/2✓ Branch 2 → 3 taken 1862 times.
✓ Branch 3 → 4 taken 1862 times.
|
1862 | const QString key = id.trimmed().toLower(); |
| 93 |
2/2✓ Branch 18 → 7 taken 15322 times.
✓ Branch 18 → 19 taken 2 times.
|
15324 | for (const Service &service : m_services) |
| 94 |
6/6✓ Branch 9 → 10 taken 13465 times.
✓ Branch 9 → 12 taken 1857 times.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 13 taken 13462 times.
✓ Branch 14 → 15 taken 1860 times.
✓ Branch 14 → 16 taken 13462 times.
|
15322 | if (service.id == key || service.aliases.contains(key)) |
| 95 | 1860 | return &service; | |
| 96 | 2 | return nullptr; | |
| 97 | 1862 | } | |
| 98 | |||
| 99 | 163 | QStringList ServiceCatalog::idsForKind(const QString &kind) const { | |
| 100 | 163 | QStringList ids; | |
| 101 |
2/2✓ Branch 10 → 4 taken 2934 times.
✓ Branch 10 → 11 taken 163 times.
|
3097 | for (const Service &service : m_services) |
| 102 |
2/2✓ Branch 6 → 7 taken 1384 times.
✓ Branch 6 → 8 taken 1550 times.
|
2934 | if (service.kind == kind) |
| 103 |
1/1✓ Branch 7 → 8 taken 1384 times.
|
1384 | ids.append(service.id); |
| 104 | 163 | return ids; | |
| 105 | ✗ | } | |
| 106 | |||
| 107 | 204 | ServiceCatalog::Recognised ServiceCatalog::recognise(const QString &input, const QString &kindHint) const { | |
| 108 |
1/1✓ Branch 2 → 3 taken 204 times.
|
204 | const QString text = input.trimmed(); |
| 109 | 204 | Recognised result; | |
| 110 |
2/2✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 203 times.
|
204 | if (text.isEmpty()) |
| 111 | 1 | return result; | |
| 112 |
2/2✓ Branch 68 → 8 taken 1313 times.
✓ Branch 68 → 69 taken 2 times.
|
1315 | for (const Service &service : m_services) { |
| 113 |
2/2✓ Branch 65 → 11 taken 2082 times.
✓ Branch 65 → 66 taken 1112 times.
|
3194 | for (const Service::Pattern &pattern : service.patterns) { |
| 114 |
6/6✓ Branch 13 → 14 taken 52 times.
✓ Branch 13 → 17 taken 2030 times.
✓ Branch 15 → 16 taken 12 times.
✓ Branch 15 → 17 taken 40 times.
✓ Branch 18 → 19 taken 12 times.
✓ Branch 18 → 20 taken 2070 times.
|
2082 | if (!pattern.requiredKind.isEmpty() && pattern.requiredKind != kindHint) |
| 115 | 1881 | continue; | |
| 116 |
1/1✓ Branch 21 → 22 taken 2070 times.
|
2070 | const QRegularExpressionMatch match = pattern.match.match(text); |
| 117 |
3/3✓ Branch 22 → 23 taken 2070 times.
✓ Branch 23 → 24 taken 1869 times.
✓ Branch 23 → 25 taken 201 times.
|
2070 | if (!match.hasMatch()) |
| 118 | 1869 | continue; | |
| 119 | 201 | result.recognised = true; | |
| 120 | 201 | result.service = service.id; | |
| 121 | 201 | result.kind = service.kind; | |
| 122 |
1/1✓ Branch 28 → 29 taken 201 times.
|
201 | result.handle = fill(service.handleTemplate, match); |
| 123 |
1/1✓ Branch 33 → 34 taken 201 times.
|
201 | result.url = fill(service.urlTemplate, match); |
| 124 |
2/2✓ Branch 39 → 40 taken 2 times.
✓ Branch 39 → 46 taken 199 times.
|
201 | if (service.kind == QLatin1String("link")) |
| 125 |
1/1✓ Branch 41 → 42 taken 2 times.
|
2 | result.handle = shortLink(result.url); |
| 126 |
1/2✓ Branch 50 → 51 taken 92 times.
✗ Branch 50 → 54 not taken.
|
293 | result.value = service.kind == QLatin1String("chat") && !service.uriTemplate.isEmpty() |
| 127 |
5/7✓ Branch 48 → 49 taken 92 times.
✓ Branch 48 → 54 taken 109 times.
✓ Branch 52 → 53 taken 92 times.
✓ Branch 57 → 58 taken 92 times.
✓ Branch 57 → 59 taken 109 times.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
|
695 | ? fill(service.uriTemplate, match) |
| 128 | 201 | : result.url; | |
| 129 | 201 | return result; | |
| 130 | 2070 | } | |
| 131 | } | ||
| 132 | 2 | return result; | |
| 133 | 204 | } | |
| 134 | |||
| 135 | 1543 | QString ServiceCatalog::displayName(const QString &serviceId, const QString &language) const { | |
| 136 |
1/1✓ Branch 2 → 3 taken 1543 times.
|
1543 | const Service *service = find(serviceId); |
| 137 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 17 taken 1542 times.
|
1543 | if (!service) { |
| 138 | // A service MailJD does not know: its stored name, readable. | ||
| 139 |
1/1✓ Branch 4 → 5 taken 1 time.
|
1 | const QString name = serviceId.trimmed(); |
| 140 |
4/8✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 15 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
|
1 | return name.isEmpty() ? QString() : name.at(0).toUpper() + name.mid(1); |
| 141 | 1 | } | |
| 142 |
9/13✓ Branch 18 → 19 taken 1541 times.
✓ Branch 18 → 22 taken 1 time.
✓ Branch 19 → 20 taken 1541 times.
✓ Branch 20 → 21 taken 1541 times.
✓ Branch 21 → 23 taken 1541 times.
✓ Branch 23 → 24 taken 1541 times.
✓ Branch 23 → 25 taken 1 time.
✓ Branch 25 → 26 taken 1541 times.
✓ Branch 25 → 27 taken 1 time.
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 48 not taken.
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 52 not taken.
|
1542 | const QString lang = language.isEmpty() ? QLocale().name().left(2) : language; |
| 143 |
3/4✓ Branch 29 → 30 taken 69 times.
✓ Branch 29 → 33 taken 1473 times.
✓ Branch 31 → 32 taken 69 times.
✗ Branch 31 → 33 not taken.
|
1542 | return lang == QLatin1String("de") && !service->nameDe.isEmpty() ? service->nameDe : service->nameEn; |
| 144 | 1542 | } | |
| 145 | |||
| 146 | 129 | ServiceCatalog::Recognised ServiceCatalog::describe(const ContactSocial &social) const { | |
| 147 | // A stored value is shown with the service it was saved under; the | ||
| 148 | // patterns only supply the handle and a link. | ||
| 149 | 129 | Recognised result = recognise(social.value, social.kind); | |
| 150 |
1/1✓ Branch 3 → 4 taken 129 times.
|
129 | const Service *stored = find(social.service); |
| 151 |
5/10✓ Branch 4 → 5 taken 129 times.
✗ Branch 4 → 11 not taken.
✓ Branch 5 → 6 taken 128 times.
✓ Branch 5 → 12 taken 1 time.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 12 taken 128 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 31 taken 129 times.
|
129 | if (!result.recognised || (stored && result.service != stored->id && result.kind == QLatin1String("link"))) { |
| 152 | ✗ | result.recognised = stored != nullptr; | |
| 153 | ✗ | result.handle = social.user.isEmpty() ? shortLink(social.value) : social.user; | |
| 154 | ✗ | result.url = social.value.startsWith(QLatin1String("http"), Qt::CaseInsensitive) ? social.value : QString(); | |
| 155 | } | ||
| 156 |
2/2✓ Branch 31 → 32 taken 128 times.
✓ Branch 31 → 34 taken 1 time.
|
129 | if (stored) { |
| 157 | 128 | result.service = stored->id; | |
| 158 | 128 | result.kind = stored->kind; | |
| 159 |
3/6✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 40 not taken.
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 40 not taken.
✓ Branch 41 → 42 taken 1 time.
✗ Branch 41 → 43 not taken.
|
1 | } else if (!social.service.isEmpty() && result.kind == QLatin1String("link")) { |
| 160 | 1 | result.service = social.service; // e.g. an Apple "flickr" profile | |
| 161 | } | ||
| 162 |
5/6✓ Branch 44 → 45 taken 45 times.
✓ Branch 44 → 49 taken 84 times.
✓ Branch 47 → 48 taken 45 times.
✗ Branch 47 → 49 not taken.
✓ Branch 50 → 51 taken 45 times.
✓ Branch 50 → 52 taken 84 times.
|
129 | if (!social.user.isEmpty() && result.kind != QLatin1String("chat")) |
| 163 | 45 | result.handle = social.user; | |
| 164 | 129 | result.value = social.value; | |
| 165 | 129 | return result; | |
| 166 | ✗ | } | |
| 167 | |||
| 168 | 2 | ContactSocial ServiceCatalog::toSocial(const Recognised &recognised) { | |
| 169 | 2 | ContactSocial social; | |
| 170 |
6/6✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 9 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 1 time.
|
4 | social.kind = recognised.kind == QLatin1String("chat") ? QStringLiteral("chat") : QStringLiteral("profile"); |
| 171 | 2 | social.service = recognised.service; | |
| 172 | 2 | social.value = recognised.value; | |
| 173 |
3/4✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 29 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 24 → 29 not taken.
|
3 | if (social.kind == QLatin1String("profile") && recognised.handle != recognised.value && |
| 174 |
3/4✓ Branch 27 → 28 taken 1 time.
✗ Branch 27 → 29 not taken.
✓ Branch 30 → 31 taken 1 time.
✓ Branch 30 → 32 taken 1 time.
|
3 | recognised.kind != QLatin1String("link")) |
| 175 | 1 | social.user = recognised.handle; | |
| 176 | 2 | return social; | |
| 177 | } | ||
| 178 |