File indexing completed on 2024-04-21 16:32:05

0001 /* This file is part of Kairo Timer
0002 
0003    SPDX-FileCopyrightText: 2016 (c) Kevin Ottens <ervin@kde.org>
0004 
0005    SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 
0007 */
0008 
0009 #include "circuitreader.h"
0010 
0011 #include <QIODevice>
0012 #include <QJsonArray>
0013 #include <QJsonDocument>
0014 #include <QJsonObject>
0015 #include <QJsonValue>
0016 #include <QTextStream>
0017 
0018 #include <functional>
0019 
0020 CircuitReader::CircuitReader(QIODevice *device)
0021     : m_device{device}
0022 {
0023 }
0024 
0025 CircuitReader::CircuitReader(CircuitReader &&other)
0026     : m_device{other.m_device},
0027       m_metaData{other.m_metaData}
0028 {
0029     other.m_device = nullptr;
0030     other.m_metaData.clear();
0031 }
0032 
0033 CircuitReader &CircuitReader::operator=(CircuitReader &&other)
0034 {
0035     m_device = other.m_device;
0036     m_metaData = other.m_metaData;
0037 
0038     other.m_device = nullptr;
0039     other.m_metaData.clear();
0040 
0041     return *this;
0042 }
0043 
0044 QIODevice *CircuitReader::device() const
0045 {
0046     return m_device;
0047 }
0048 
0049 CircuitReader::MetaData CircuitReader::readMetaData()
0050 {
0051     if (!m_device)
0052         return {};
0053 
0054     if (!m_device->isOpen() && !m_device->open(QIODevice::ReadOnly))
0055         return {};
0056 
0057     if (m_device->pos() > 0)
0058         return m_metaData;
0059 
0060     auto line = QString::fromUtf8(m_device->readLine()).trimmed();
0061     if (line != QStringLiteral("---"))
0062         return {};
0063 
0064     auto metaData = MetaData{};
0065     line = QString::fromUtf8(m_device->readLine()).trimmed();
0066     while (!m_device->atEnd() && line != QStringLiteral("---")) {
0067         const auto columnIndex = line.indexOf(':');
0068         if (columnIndex < 0)
0069             return {};
0070 
0071         const auto key = line.left(columnIndex).simplified();
0072         const auto value = line.mid(columnIndex + 1).simplified();
0073         metaData.insert(key, value);
0074 
0075         line = QString::fromUtf8(m_device->readLine()).trimmed();
0076     }
0077 
0078     if (line != QStringLiteral("---"))
0079         return {};
0080 
0081     m_metaData = metaData;
0082 
0083     return m_metaData;
0084 }
0085 
0086 namespace {
0087     bool isLoopObject(const QJsonObject &object)
0088     {
0089         return object.contains("content") && object.contains("count");
0090     }
0091 
0092     bool isTimerObject(const QJsonObject &object)
0093     {
0094         return object.contains("text") && !isLoopObject(object);
0095     }
0096 
0097     TimerModel convertTimerObject(const QJsonObject &object)
0098     {
0099         return {
0100             object.value("text").toString(),
0101             object.value("duration").toInt()
0102         };
0103     }
0104 
0105     QVector<TimerModel> convertLoopObject(const QJsonObject &object)
0106     {
0107         const auto count = object.value("count").toInt();
0108         if (count <= 0)
0109             return {};
0110 
0111         const auto content = object.value("content");
0112         if (!content.isArray())
0113             return {};
0114 
0115         auto array = content.toArray();
0116         if (array.isEmpty())
0117             return {};
0118 
0119         const auto end = std::remove_if(array.begin(), array.end(),
0120                                         [] (const QJsonValue &value) {
0121                                             return !value.isObject() || !isTimerObject(value.toObject());
0122                                         });
0123 
0124         const auto text = object.value("text").toString();
0125         const auto prefix = text.isEmpty() ? QString{} : text + ", ";
0126 
0127         auto loop = QVector<TimerModel>{};
0128 
0129         for (int i = 0; i < count; i++) {
0130             auto timers = QVector<TimerModel>{};
0131             std::transform(array.begin(), end, std::back_inserter(timers),
0132                            [prefix, i, count] (const QJsonValue &value) {
0133                                const auto timer = convertTimerObject(value.toObject());
0134                                return TimerModel{
0135                                    QString("%1%2 (%3/%4)").arg(prefix).arg(timer.text()).arg(i + 1).arg(count),
0136                                    timer.duration()
0137                                };
0138                            });
0139             loop += timers;
0140         }
0141 
0142         const auto removeLast = object.value("removeLast").toBool();
0143         if (removeLast)
0144             loop.removeLast();
0145 
0146         return loop;
0147     }
0148 }
0149 
0150 CircuitModel CircuitReader::readCircuit()
0151 {
0152     if (!m_device)
0153         return {};
0154 
0155     if (!m_device->isOpen())
0156         readMetaData();
0157 
0158     auto content = QVector<TimerModel>{};
0159 
0160     const auto data = m_device->readAll();
0161     const auto json = QJsonDocument::fromJson(data);
0162     if (json.isArray()) {
0163         const auto array = json.array();
0164         const auto end = std::remove_if(array.begin(), array.end(),
0165                                         [] (const QJsonValue &value) { return !value.isObject(); });
0166 
0167         auto objects = QVector<QJsonObject>{};
0168         auto toObject = std::mem_fn(static_cast<QJsonObject(QJsonValue::*)() const>(&QJsonValue::toObject));
0169         std::transform(array.begin(), end, std::back_inserter(objects), toObject);
0170 
0171         content = std::accumulate(objects.begin(), objects.end(), content,
0172                                   [] (const QVector<TimerModel> &content, const QJsonObject &object) {
0173                                       auto timers = content;
0174                                       if (isTimerObject(object))
0175                                           timers << convertTimerObject(object);
0176                                       else if (isLoopObject(object))
0177                                           timers += convertLoopObject(object);
0178                                       return timers;
0179                                   });
0180     }
0181 
0182     return {m_metaData.value("name").toString(), content};
0183 }