|
1 | 1 | #include "RecentFileAddOn.h" |
2 | 2 |
|
| 3 | +#include <exception> |
3 | 4 | #include <memory> |
4 | 5 |
|
5 | 6 | #include <QDir> |
|
20 | 21 |
|
21 | 22 | #include <QAKQuick/quickactioncontext.h> |
22 | 23 |
|
| 24 | +#include <SVSCraftQuick/MessageBox.h> |
| 25 | + |
23 | 26 | #include <uishell/USDef.h> |
24 | 27 |
|
25 | 28 | #include <coreplugin/CoreInterface.h> |
@@ -154,6 +157,76 @@ namespace Core::Internal { |
154 | 157 | return QDir(recoveryRootPath()).filePath(QStringLiteral("latest_unsaved_project.dspx")); |
155 | 158 | } |
156 | 159 |
|
| 160 | + static void showRecoveryRestoreFailure() { |
| 161 | + SVS::MessageBox::critical( |
| 162 | + RuntimeInterface::qmlEngine(), |
| 163 | + nullptr, |
| 164 | + RecentFileAddOn::tr("Failed to restore recovery file"), |
| 165 | + RecentFileAddOn::tr("Cannot restore the recovery file.") |
| 166 | + ); |
| 167 | + } |
| 168 | + |
| 169 | + static void showPartialRecoveryWarning() { |
| 170 | + SVS::MessageBox::warning( |
| 171 | + RuntimeInterface::qmlEngine(), |
| 172 | + nullptr, |
| 173 | + RecentFileAddOn::tr("Complete recovery failed"), |
| 174 | + RecentFileAddOn::tr("Complete recovery failed. Partial recovery will be attempted."), |
| 175 | + SVS::SVSCraft::Ok, |
| 176 | + SVS::SVSCraft::Ok |
| 177 | + ); |
| 178 | + } |
| 179 | + |
| 180 | + static std::unique_ptr<dspx::Document> restoreRecoveryDocument(const QDir &recoveryDirectory, |
| 181 | + dspx::Document::RestoreOptions options, |
| 182 | + QString *errorMessage) { |
| 183 | + QFile snapshotFile(recoveryDirectory.filePath(QStringLiteral("snapshot"))); |
| 184 | + if (!snapshotFile.open(QIODevice::ReadOnly)) { |
| 185 | + if (errorMessage) { |
| 186 | + *errorMessage = QStringLiteral("Failed to open recovery snapshot \"%1\": %2") |
| 187 | + .arg(snapshotFile.fileName(), snapshotFile.errorString()); |
| 188 | + } |
| 189 | + return nullptr; |
| 190 | + } |
| 191 | + |
| 192 | + QFile logsFile(recoveryDirectory.filePath(QStringLiteral("logs"))); |
| 193 | + QIODevice *logsDevice = nullptr; |
| 194 | + if (logsFile.exists()) { |
| 195 | + if (!logsFile.open(QIODevice::ReadOnly)) { |
| 196 | + if (errorMessage) { |
| 197 | + *errorMessage = QStringLiteral("Failed to open recovery log \"%1\": %2") |
| 198 | + .arg(logsFile.fileName(), logsFile.errorString()); |
| 199 | + } |
| 200 | + return nullptr; |
| 201 | + } |
| 202 | + logsDevice = &logsFile; |
| 203 | + } |
| 204 | + |
| 205 | + try { |
| 206 | + auto *document = dspx::Document::restore(&snapshotFile, logsDevice, options); |
| 207 | + if (!document) { |
| 208 | + if (errorMessage) { |
| 209 | + *errorMessage = QStringLiteral("Document::restore returned null for recovery directory \"%1\"") |
| 210 | + .arg(recoveryDirectory.absolutePath()); |
| 211 | + } |
| 212 | + return nullptr; |
| 213 | + } |
| 214 | + return std::unique_ptr<dspx::Document>(document); |
| 215 | + } catch (const std::exception &e) { |
| 216 | + if (errorMessage) { |
| 217 | + *errorMessage = QStringLiteral("Failed to restore document from recovery directory \"%1\": %2") |
| 218 | + .arg(recoveryDirectory.absolutePath(), QString::fromUtf8(e.what())); |
| 219 | + } |
| 220 | + return nullptr; |
| 221 | + } catch (...) { |
| 222 | + if (errorMessage) { |
| 223 | + *errorMessage = QStringLiteral("Failed to restore document from recovery directory \"%1\": unknown exception") |
| 224 | + .arg(recoveryDirectory.absolutePath()); |
| 225 | + } |
| 226 | + return nullptr; |
| 227 | + } |
| 228 | + } |
| 229 | + |
157 | 230 | static bool recoveryDirectoryInUse(const QDir &directory) { |
158 | 231 | QLockFile lock(directory.filePath(QStringLiteral("active.lock"))); |
159 | 232 | if (lock.tryLock(0)) { |
@@ -262,32 +335,44 @@ namespace Core::Internal { |
262 | 335 | } |
263 | 336 |
|
264 | 337 | QDir recoveryDirectory(path); |
265 | | - QFile snapshotFile(recoveryDirectory.filePath(QStringLiteral("snapshot"))); |
266 | | - if (!snapshotFile.open(QIODevice::ReadOnly)) { |
267 | | - qCWarning(lcRecentFileAddOn) << "Failed to open recovery snapshot:" << snapshotFile.fileName() << snapshotFile.errorString(); |
268 | | - return; |
269 | | - } |
270 | | - |
271 | | - QFile logsFile(recoveryDirectory.filePath(QStringLiteral("logs"))); |
272 | | - QIODevice *logsDevice = nullptr; |
273 | | - if (logsFile.exists()) { |
274 | | - if (!logsFile.open(QIODevice::ReadOnly)) { |
275 | | - qCWarning(lcRecentFileAddOn) << "Failed to open recovery log:" << logsFile.fileName() << logsFile.errorString(); |
| 338 | + QString completeRecoveryError; |
| 339 | + auto restoredDocument = restoreRecoveryDocument(recoveryDirectory, {}, &completeRecoveryError); |
| 340 | + if (!restoredDocument) { |
| 341 | + qCCritical(lcRecentFileAddOn) << "Complete recovery failed:" << completeRecoveryError; |
| 342 | + showPartialRecoveryWarning(); |
| 343 | + |
| 344 | + QString partialRecoveryError; |
| 345 | + restoredDocument = restoreRecoveryDocument( |
| 346 | + recoveryDirectory, |
| 347 | + dspx::Document::RO_DiscardInvalidCommitLogTail, |
| 348 | + &partialRecoveryError |
| 349 | + ); |
| 350 | + if (!restoredDocument) { |
| 351 | + qCCritical(lcRecentFileAddOn) << "Partial recovery failed:" << partialRecoveryError; |
| 352 | + showRecoveryRestoreFailure(); |
276 | 353 | return; |
277 | 354 | } |
278 | | - logsDevice = &logsFile; |
| 355 | + qCWarning(lcRecentFileAddOn) << "Partial recovery succeeded after complete recovery failed:" |
| 356 | + << recoveryDirectory.absolutePath(); |
279 | 357 | } |
280 | 358 |
|
281 | | - std::unique_ptr<dspx::Document> restoredDocument; |
| 359 | + opendspx::Model openDspxModel; |
282 | 360 | try { |
283 | | - restoredDocument.reset(dspx::Document::restore(&snapshotFile, logsDevice)); |
| 361 | + dspx::Model restoredModel(restoredDocument.get()); |
| 362 | + openDspxModel = restoredModel.toOpenDSPX(); |
| 363 | + } catch (const std::exception &e) { |
| 364 | + qCCritical(lcRecentFileAddOn) << "Failed to convert restored recovery document to OpenDSPX:" |
| 365 | + << recoveryDirectory.absolutePath() |
| 366 | + << e.what(); |
| 367 | + showRecoveryRestoreFailure(); |
| 368 | + return; |
284 | 369 | } catch (...) { |
285 | | - qCWarning(lcRecentFileAddOn) << "Failed to restore document log:" << recoveryDirectory.absolutePath(); |
| 370 | + qCCritical(lcRecentFileAddOn) << "Failed to convert restored recovery document to OpenDSPX:" |
| 371 | + << recoveryDirectory.absolutePath() |
| 372 | + << "Unknown exception"; |
| 373 | + showRecoveryRestoreFailure(); |
286 | 374 | return; |
287 | 375 | } |
288 | | - |
289 | | - dspx::Model restoredModel(restoredDocument.get()); |
290 | | - auto openDspxModel = restoredModel.toOpenDSPX(); |
291 | 376 | auto defaultDocumentName = defaultDocumentNameFromRecoveryName(item->data(UIShell::USDef::RF_NameRole).toString()); |
292 | 377 | auto projectDocumentContext = std::make_unique<ProjectDocumentContext>(); |
293 | 378 | if (!projectDocumentContext->newFile(openDspxModel, defaultDocumentName, false)) { |
|
0 commit comments