File indexing completed on 2024-05-12 04:01:32

0001 /*
0002     SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "mecard.h"
0008 
0009 #include <vector>
0010 
0011 using namespace Prison;
0012 
0013 class Prison::MeCardPrivate
0014 {
0015 public:
0016     QStringView header;
0017     struct Element {
0018         QStringView key;
0019         QStringList values;
0020         bool operator<(QStringView other) const;
0021     };
0022     std::vector<Element> elements;
0023 };
0024 
0025 bool MeCardPrivate::Element::operator<(QStringView other) const
0026 {
0027     return key < other;
0028 }
0029 
0030 MeCard::MeCard()
0031     : d(new MeCardPrivate())
0032 {
0033 }
0034 
0035 MeCard::MeCard(MeCard &&other) noexcept
0036     : d(std::move(other.d))
0037 {
0038     other.d.reset();
0039 }
0040 
0041 MeCard &MeCard::operator=(MeCard &&other) noexcept
0042 {
0043     std::swap(d, other.d);
0044     return *this;
0045 }
0046 
0047 MeCard::~MeCard() = default;
0048 
0049 std::optional<MeCard> MeCard::parse(const QString &data)
0050 {
0051     const auto idx = data.indexOf(QLatin1Char(':'));
0052     if (idx <= 0 || idx >= data.size() - 1) {
0053         return std::nullopt;
0054     }
0055 
0056     MeCard m;
0057     m.d->header = QStringView(data).left(idx);
0058 
0059     auto remaining = QStringView(data).mid(idx + 1);
0060     while (remaining.size() > 0) {
0061         const auto keyIdx = remaining.indexOf(QLatin1Char(':'));
0062         if (keyIdx <= 0 || keyIdx + 2 >= remaining.size()) {
0063             break;
0064         }
0065 
0066         QString value;
0067         auto elemIdx = keyIdx + 1;
0068         bool inQuote = false;
0069         for (; elemIdx < remaining.size() - 1; ++elemIdx) {
0070             auto c = remaining.at(elemIdx);
0071             if (elemIdx == (keyIdx + 1) && c == QLatin1Char('"')) { // leading quote
0072                 inQuote = true;
0073                 continue;
0074             }
0075             if (inQuote && c == QLatin1Char('"') && remaining.at(elemIdx + 1) == QLatin1Char(';')) { // trailing quote
0076                 break;
0077             }
0078             if (c == QLatin1Char(';')) { // end of element
0079                 break;
0080             }
0081             if (c == QLatin1Char('\\')) { // quoted character
0082                 ++elemIdx;
0083                 c = remaining.at(elemIdx);
0084             }
0085             value.push_back(c);
0086         }
0087 
0088         const auto key = remaining.left(keyIdx);
0089         auto it = std::lower_bound(m.d->elements.begin(), m.d->elements.end(), key);
0090         if (it == m.d->elements.end()) {
0091             m.d->elements.push_back(MeCardPrivate::Element());
0092             it = std::prev(m.d->elements.end());
0093         } else if ((*it).key != key) {
0094             it = m.d->elements.insert(it, MeCardPrivate::Element());
0095         }
0096         (*it).key = key;
0097         (*it).values.push_back(value);
0098 
0099         remaining = remaining.mid(elemIdx + 1);
0100     }
0101 
0102     if (m.d->elements.empty()) {
0103         return std::nullopt;
0104     }
0105 
0106     return m;
0107 }
0108 
0109 QString MeCard::header() const
0110 {
0111     return d->header.toString();
0112 }
0113 
0114 QStringView MeCard::headerView() const
0115 {
0116     return d->header;
0117 }
0118 
0119 QString MeCard::value(QStringView key) const
0120 {
0121     const auto it = std::lower_bound(d->elements.begin(), d->elements.end(), key);
0122     if (it != d->elements.end() && (*it).key == key && (*it).values.size() == 1) {
0123         return (*it).values.at(0);
0124     }
0125     return {};
0126 }
0127 
0128 QStringList MeCard::values(QStringView key) const
0129 {
0130     const auto it = std::lower_bound(d->elements.begin(), d->elements.end(), key);
0131     if (it != d->elements.end() && (*it).key == key) {
0132         return (*it).values;
0133     }
0134     return {};
0135 }
0136 
0137 QVariantMap MeCard::toVariantMap() const
0138 {
0139     QVariantMap map;
0140     for (const auto &element : std::as_const(d->elements)) {
0141         if (element.values.size() > 1) {
0142             map.insert(element.key.toString(), element.values);
0143         } else {
0144             map.insert(element.key.toString(), element.values.at(0));
0145         }
0146     }
0147     return map;
0148 }