1 #include "./syncthingdir.h"
2
3 #include <c++utilities/conversion/stringconversion.h>
4
5 #include <QCoreApplication>
6 #include <QJsonObject>
7 #include <QStringBuilder>
8
9 using namespace CppUtilities;
10
11 namespace Data {
12
statusString(SyncthingDirStatus status)13 QString statusString(SyncthingDirStatus status)
14 {
15 switch (status) {
16 case SyncthingDirStatus::Unknown:
17 return QCoreApplication::translate("SyncthingDirStatus", "unknown");
18 case SyncthingDirStatus::Idle:
19 return QCoreApplication::translate("SyncthingDirStatus", "idle");
20 case SyncthingDirStatus::Scanning:
21 return QCoreApplication::translate("SyncthingDirStatus", "scanning");
22 case SyncthingDirStatus::WaitingToScan:
23 return QCoreApplication::translate("SyncthingDirStatus", "waiting to scan");
24 case SyncthingDirStatus::WaitingToSync:
25 return QCoreApplication::translate("SyncthingDirStatus", "waiting to sync");
26 case SyncthingDirStatus::PreparingToSync:
27 return QCoreApplication::translate("SyncthingDirStatus", "preparing to sync");
28 case SyncthingDirStatus::Synchronizing:
29 return QCoreApplication::translate("SyncthingDirStatus", "synchronizing");
30 case SyncthingDirStatus::Cleaning:
31 return QCoreApplication::translate("SyncthingDirStatus", "cleaning");
32 case SyncthingDirStatus::WaitingToClean:
33 return QCoreApplication::translate("SyncthingDirStatus", "waiting to clean");
34 case SyncthingDirStatus::OutOfSync:
35 return QCoreApplication::translate("SyncthingDirStatus", "out of sync");
36 }
37 return QString();
38 }
39
dirTypeString(SyncthingDirType dirType)40 QString dirTypeString(SyncthingDirType dirType)
41 {
42 switch (dirType) {
43 case SyncthingDirType::Unknown:
44 return QCoreApplication::translate("SyncthingDirType", "unknown");
45 case SyncthingDirType::SendReceive:
46 return QCoreApplication::translate("SyncthingDirType", "Send & Receive");
47 case SyncthingDirType::SendOnly:
48 return QCoreApplication::translate("SyncthingDirType", "Send only");
49 case SyncthingDirType::ReceiveOnly:
50 return QCoreApplication::translate("SyncthingDirType", "Receive only");
51 }
52 return QString();
53 }
54
checkWhetherStatusUpdateRelevant(DateTime time)55 bool SyncthingDir::checkWhetherStatusUpdateRelevant(DateTime time)
56 {
57 // ignore old updates
58 if (lastStatusUpdate > time) {
59 return false;
60 }
61 lastStatusUpdate = time;
62 return true;
63 }
64
finalizeStatusUpdate(SyncthingDirStatus newStatus,DateTime time)65 bool SyncthingDir::finalizeStatusUpdate(SyncthingDirStatus newStatus, DateTime time)
66 {
67 // handle obsoletion of out-of-sync items: no FolderErrors are accepted older than the last "sync" state are accepted
68 if (newStatus == SyncthingDirStatus::PreparingToSync || newStatus == SyncthingDirStatus::Synchronizing) {
69 // update time of last "sync" state and obsolete currently assigned errors
70 lastSyncStarted = time; // used internally and not displayed, hence keep it GMT
71 itemErrors.clear();
72 pullErrorCount = 0;
73 } else if (lastSyncStarted.isNull() && newStatus != SyncthingDirStatus::OutOfSync) {
74 // prevent adding new errors from "before the first status" if the time of the last "sync" state is unknown
75 lastSyncStarted = time;
76 }
77
78 // clear global error if not out-of-sync anymore
79 if (newStatus != SyncthingDirStatus::OutOfSync) {
80 globalError.clear();
81 }
82
83 // consider the directory still as out-of-sync if there are still pull errors
84 // note: Syncthing reports status changes to "idle" despite pull errors. This means we can only rely on reading
85 // a "FolderSummary" event without pull errors for clearing the out-of-sync status.
86 if (pullErrorCount && (newStatus == SyncthingDirStatus::Unknown || newStatus == SyncthingDirStatus::Idle)) {
87 newStatus = SyncthingDirStatus::OutOfSync;
88 }
89
90 if (newStatus == status) {
91 return false;
92 }
93
94 // update last scan time if the previous status was scanning
95 if (status == SyncthingDirStatus::Scanning) {
96 // FIXME: better use \a time and convert it from GMT to local time
97 lastScanTime = DateTime::now();
98 }
99
100 status = newStatus;
101 return true;
102 }
103
104 /*!
105 * \brief Assigns the status from the specified status string.
106 * \returns Returns whether the status has actually changed.
107 */
assignStatus(const QString & statusStr,CppUtilities::DateTime time)108 bool SyncthingDir::assignStatus(const QString &statusStr, CppUtilities::DateTime time)
109 {
110 if (!checkWhetherStatusUpdateRelevant(time)) {
111 return false;
112 }
113
114 // identify statusStr
115 SyncthingDirStatus newStatus;
116 if (statusStr == QLatin1String("idle")) {
117 completionPercentage = 0;
118 newStatus = SyncthingDirStatus::Idle;
119 } else if (statusStr == QLatin1String("scanning")) {
120 newStatus = SyncthingDirStatus::Scanning;
121 } else if (statusStr == QLatin1String("scan-waiting")) {
122 newStatus = SyncthingDirStatus::WaitingToScan;
123 } else if (statusStr == QLatin1String("sync-waiting")) {
124 newStatus = SyncthingDirStatus::WaitingToSync;
125 } else if (statusStr == QLatin1String("sync-preparing")) {
126 // ensure status changed signal is emitted
127 if (!itemErrors.empty()) {
128 status = SyncthingDirStatus::Unknown;
129 }
130 newStatus = SyncthingDirStatus::PreparingToSync;
131 } else if (statusStr == QLatin1String("syncing")) {
132 // ensure status changed signal is emitted
133 if (!itemErrors.empty()) {
134 status = SyncthingDirStatus::Unknown;
135 }
136 newStatus = SyncthingDirStatus::Synchronizing;
137 } else if (statusStr == QLatin1String("cleaning")) {
138 newStatus = SyncthingDirStatus::Cleaning;
139 } else if (statusStr == QLatin1String("clean-waiting")) {
140 newStatus = SyncthingDirStatus::WaitingToClean;
141 } else if (statusStr == QLatin1String("error")) {
142 completionPercentage = 0;
143 newStatus = SyncthingDirStatus::OutOfSync;
144 } else {
145 newStatus = SyncthingDirStatus::Idle;
146 }
147
148 rawStatus = statusStr;
149
150 return finalizeStatusUpdate(newStatus, time);
151 }
152
assignDirType(const QString & dirTypeStr)153 bool SyncthingDir::assignDirType(const QString &dirTypeStr)
154 {
155 if (dirTypeStr == QLatin1String("sendreceive") || dirTypeStr == QLatin1String("readwrite")) {
156 dirType = SyncthingDirType::SendReceive;
157 } else if (dirTypeStr == QLatin1String("sendonly") || dirTypeStr == QLatin1String("readonly")) {
158 dirType = SyncthingDirType::SendOnly;
159 } else if (dirTypeStr == QLatin1String("receiveonly")) {
160 dirType = SyncthingDirType::ReceiveOnly;
161 } else {
162 dirType = SyncthingDirType::Unknown;
163 return false;
164 }
165 return true;
166 }
167
statusString() const168 QString SyncthingDir::statusString() const
169 {
170 if (paused) {
171 return QCoreApplication::translate("SyncthingDir", "paused");
172 } else if (isUnshared()) {
173 return QCoreApplication::translate("SyncthingDir", "unshared");
174 } else if (status == SyncthingDirStatus::Unknown && !rawStatus.isEmpty()) {
175 return QString(rawStatus);
176 } else {
177 return ::Data::statusString(status);
178 }
179 }
180
pathWithoutTrailingSlash() const181 QtUtilities::StringView SyncthingDir::pathWithoutTrailingSlash() const
182 {
183 auto dirPath = QtUtilities::makeStringView(path);
184 while (dirPath.endsWith(QChar('/'))) {
185 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
186 dirPath.chop(1);
187 #else
188 dirPath = dirPath.left(dirPath.size() - 1);
189 #endif
190 }
191 return dirPath;
192 }
193
areRemotesUpToDate() const194 bool SyncthingDir::areRemotesUpToDate() const
195 {
196 for (const auto &completionForDev : completionByDevice) {
197 if (!completionForDev.second.needed.isNull()) {
198 return false;
199 }
200 }
201 return true;
202 }
203
SyncthingItemDownloadProgress(const QString & containingDirPath,const QString & relativeItemPath,const QJsonObject & values)204 SyncthingItemDownloadProgress::SyncthingItemDownloadProgress(
205 const QString &containingDirPath, const QString &relativeItemPath, const QJsonObject &values)
206 : relativePath(relativeItemPath)
207 , fileInfo(containingDirPath % QChar('/') % QString(relativeItemPath).replace(QChar('\\'), QChar('/')))
208 , blocksCurrentlyDownloading(values.value(QLatin1String("Pulling")).toInt())
209 , blocksAlreadyDownloaded(values.value(QLatin1String("Pulled")).toInt())
210 , totalNumberOfBlocks(values.value(QLatin1String("Total")).toInt())
211 , downloadPercentage((blocksAlreadyDownloaded > 0 && totalNumberOfBlocks > 0)
212 ? (static_cast<unsigned int>(blocksAlreadyDownloaded) * 100 / static_cast<unsigned int>(totalNumberOfBlocks))
213 : 0)
214 , blocksCopiedFromOrigin(values.value(QLatin1String("CopiedFromOrigin")).toInt())
215 , blocksCopiedFromElsewhere(values.value(QLatin1String("CopiedFromElsewhere")).toInt())
216 , blocksReused(values.value(QLatin1String("Reused")).toInt())
217 , bytesAlreadyHandled(values.value(QLatin1String("BytesDone")).toInt())
218 , totalNumberOfBytes(values.value(QLatin1String("BytesTotal")).toInt())
219 , label(QStringLiteral("%1 / %2 - %3 %")
220 .arg(QString::fromLatin1(
221 dataSizeToString(blocksAlreadyDownloaded > 0 ? static_cast<std::uint64_t>(blocksAlreadyDownloaded) * syncthingBlockSize : 0)
222 .data()),
223 QString::fromLatin1(
224 dataSizeToString(totalNumberOfBlocks > 0 ? static_cast<std::uint64_t>(totalNumberOfBlocks) * syncthingBlockSize : 0).data()),
225 QString::number(downloadPercentage)))
226 {
227 }
228
operator +=(const SyncthingStatistics & other)229 SyncthingStatistics &SyncthingStatistics::operator+=(const SyncthingStatistics &other)
230 {
231 bytes += other.bytes;
232 deletes += other.deletes;
233 dirs += other.dirs;
234 files += other.files;
235 symlinks += other.symlinks;
236 return *this;
237 }
238
239 /*!
240 * \brief Computes overall statistics for the specified \a directories.
241 */
SyncthingOverallDirStatistics(const std::vector<SyncthingDir> & directories)242 SyncthingOverallDirStatistics::SyncthingOverallDirStatistics(const std::vector<SyncthingDir> &directories)
243 {
244 for (const auto &dir : directories) {
245 local += dir.localStats;
246 global += dir.globalStats;
247 needed += dir.neededStats;
248 }
249 }
250
251 } // namespace Data
252