File indexing completed on 2024-04-28 13:43:44
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 }