forked from qt-creator/qt-creator
Utils: Add PersistentCacheStore
Change-Id: I952e0271afcc0fd4b03ef75fa5acb219be153290 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -117,6 +117,7 @@ add_qtc_library(Utils
|
||||
passworddialog.cpp passworddialog.h
|
||||
pathchooser.cpp pathchooser.h
|
||||
pathlisteditor.cpp pathlisteditor.h
|
||||
persistentcachestore.cpp persistentcachestore.h
|
||||
persistentsettings.cpp persistentsettings.h
|
||||
pointeralgorithm.h
|
||||
port.cpp port.h
|
||||
|
@@ -749,6 +749,11 @@ FilePath FilePath::withExecutableSuffix() const
|
||||
return withNewPath(OsSpecificAspects::withExecutableSuffix(osType(), path()));
|
||||
}
|
||||
|
||||
FilePath FilePath::withSuffix(const QString &suffix) const
|
||||
{
|
||||
return withNewPath(path() + suffix);
|
||||
}
|
||||
|
||||
static bool startsWithWindowsDriveLetterAndSlash(QStringView path)
|
||||
{
|
||||
return path.size() > 2 && path[1] == ':' && path[2] == '/' && isWindowsDriveLetter(path[0]);
|
||||
|
@@ -160,6 +160,7 @@ public:
|
||||
[[nodiscard]] FilePath symLinkTarget() const;
|
||||
[[nodiscard]] FilePath resolveSymlinks() const;
|
||||
[[nodiscard]] FilePath withExecutableSuffix() const;
|
||||
[[nodiscard]] FilePath withSuffix(const QString &suffix) const;
|
||||
[[nodiscard]] FilePath relativeChildPath(const FilePath &parent) const;
|
||||
[[nodiscard]] FilePath relativePathFrom(const FilePath &anchor) const;
|
||||
[[nodiscard]] Environment deviceEnvironment() const;
|
||||
|
115
src/libs/utils/persistentcachestore.cpp
Normal file
115
src/libs/utils/persistentcachestore.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "persistentcachestore.h"
|
||||
|
||||
#include "filepath.h"
|
||||
#include "fileutils.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class PrivateGlobal
|
||||
{
|
||||
public:
|
||||
QMutex mutex;
|
||||
QMap<Key, Store> caches;
|
||||
};
|
||||
|
||||
static expected_str<FilePath> cacheFolder()
|
||||
{
|
||||
static const FilePath folder = FilePath::fromUserInput(QStandardPaths::writableLocation(
|
||||
QStandardPaths::CacheLocation))
|
||||
/ "CachedStores";
|
||||
static expected_str<void> created = folder.ensureWritableDir();
|
||||
static expected_str<FilePath> result = created ? folder
|
||||
: expected_str<FilePath>(
|
||||
make_unexpected(created.error()));
|
||||
|
||||
QTC_ASSERT_EXPECTED(result, return result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PrivateGlobal &globals()
|
||||
{
|
||||
static PrivateGlobal global;
|
||||
return global;
|
||||
}
|
||||
|
||||
static expected_str<FilePath> filePathFromKey(const Key &cacheKey)
|
||||
{
|
||||
static const expected_str<FilePath> folder = cacheFolder();
|
||||
if (!folder)
|
||||
return folder;
|
||||
|
||||
return (*folder / FileUtils::fileSystemFriendlyName(stringFromKey(cacheKey))).withSuffix(".json");
|
||||
}
|
||||
|
||||
expected_str<Store> PersistentCacheStore::byKey(const Key &cacheKey)
|
||||
{
|
||||
const expected_str<FilePath> path = filePathFromKey(cacheKey);
|
||||
if (!path)
|
||||
return make_unexpected(path.error());
|
||||
|
||||
QMutexLocker locker(&globals().mutex);
|
||||
|
||||
auto it = globals().caches.find(cacheKey);
|
||||
if (it != globals().caches.end())
|
||||
return it.value();
|
||||
|
||||
const expected_str<QByteArray> contents = path->fileContents();
|
||||
if (!contents)
|
||||
return make_unexpected(contents.error());
|
||||
|
||||
auto result = storeFromJson(*contents);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
if (result->value("__cache_key__").toString() != stringFromKey(cacheKey)) {
|
||||
return make_unexpected(QString("Cache key mismatch: \"%1\" to \"%2\" in \"%3\".")
|
||||
.arg(stringFromKey(cacheKey))
|
||||
.arg(result->value("__cache_key__").toString())
|
||||
.arg(path->toUserOutput()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
expected_str<void> PersistentCacheStore::write(const Key &cacheKey, const Store &store)
|
||||
{
|
||||
const expected_str<FilePath> path = filePathFromKey(cacheKey);
|
||||
if (!path)
|
||||
return make_unexpected(path.error());
|
||||
|
||||
QMutexLocker locker(&globals().mutex);
|
||||
globals().caches.insert(cacheKey, store);
|
||||
|
||||
// TODO: The writing of the store data could be done in a separate thread in the future.
|
||||
Store storeCopy = store;
|
||||
storeCopy.insert("__cache_key__", stringFromKey(cacheKey));
|
||||
storeCopy.insert("__last_modified__", QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
QByteArray json = jsonFromStore(storeCopy);
|
||||
const expected_str<qint64> result = path->writeFileContents(json);
|
||||
if (!result)
|
||||
return make_unexpected(result.error());
|
||||
return {};
|
||||
}
|
||||
|
||||
expected_str<void> PersistentCacheStore::clear(const Key &cacheKey)
|
||||
{
|
||||
const expected_str<FilePath> path = filePathFromKey(cacheKey);
|
||||
if (!path)
|
||||
return make_unexpected(path.error());
|
||||
|
||||
QMutexLocker locker(&globals().mutex);
|
||||
globals().caches.remove(cacheKey);
|
||||
|
||||
if (!path->removeFile())
|
||||
return make_unexpected(QString("Failed to remove cache file."));
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Utils
|
22
src/libs/utils/persistentcachestore.h
Normal file
22
src/libs/utils/persistentcachestore.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
#include "expected.h"
|
||||
#include "store.h"
|
||||
#include "storekey.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT PersistentCacheStore
|
||||
{
|
||||
public:
|
||||
static expected_str<Store> byKey(const Key &cacheKey);
|
||||
static expected_str<void> write(const Key &cacheKey, const Store &store);
|
||||
static expected_str<void> clear(const Key &cacheKey);
|
||||
};
|
||||
|
||||
} // namespace Utils
|
@@ -6,6 +6,9 @@
|
||||
#include "algorithm.h"
|
||||
#include "qtcassert.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
KeyList keysFromStrings(const QStringList &list)
|
||||
@@ -111,4 +114,23 @@ Key numberedKey(const Key &key, int number)
|
||||
return key + Key::number(number);
|
||||
}
|
||||
|
||||
expected_str<Store> storeFromJson(const QByteArray &json)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
return make_unexpected(error.errorString());
|
||||
|
||||
if (!doc.isObject())
|
||||
return make_unexpected(QString("Not a valid JSON object."));
|
||||
|
||||
return storeFromMap(doc.toVariant().toMap());
|
||||
}
|
||||
|
||||
QByteArray jsonFromStore(const Store &store)
|
||||
{
|
||||
QJsonDocument doc = QJsonDocument::fromVariant(mapFromStore(store));
|
||||
return doc.toJson();
|
||||
}
|
||||
|
||||
} // Utils
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "expected.h"
|
||||
#include "storekey.h"
|
||||
|
||||
#include <QMap>
|
||||
@@ -27,6 +28,9 @@ QTCREATOR_UTILS_EXPORT bool isStore(const QVariant &value);
|
||||
|
||||
QTCREATOR_UTILS_EXPORT Key numberedKey(const Key &key, int number);
|
||||
|
||||
QTCREATOR_UTILS_EXPORT expected_str<Store> storeFromJson(const QByteArray &json);
|
||||
QTCREATOR_UTILS_EXPORT QByteArray jsonFromStore(const Store &store);
|
||||
|
||||
} // Utils
|
||||
|
||||
Q_DECLARE_METATYPE(Utils::Store)
|
||||
|
@@ -226,6 +226,8 @@ Project {
|
||||
"pathchooser.h",
|
||||
"pathlisteditor.cpp",
|
||||
"pathlisteditor.h",
|
||||
"persistentcachestore.cpp",
|
||||
"persistentcachestore.h",
|
||||
"persistentsettings.cpp",
|
||||
"persistentsettings.h",
|
||||
"pointeralgorithm.h",
|
||||
|
Reference in New Issue
Block a user