Skip to content

Commit 12bb41f

Browse files
committed
Add storage batch process module
1 parent f5584c1 commit 12bb41f

13 files changed

Lines changed: 1268 additions & 0 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// SPDX-FileCopyrightText: Team OpenVPI
2+
// SPDX-License-Identifier: LGPL-3.0-or-later
3+
4+
#include "Storage.h"
5+
#include "Storage_p.h"
6+
7+
#include <exception>
8+
#include <utility>
9+
10+
#include <QScopedValueRollback>
11+
12+
namespace BatchProcess {
13+
14+
StoragePrivate::StoragePrivate(Storage *q) : q_ptr(q) {
15+
}
16+
17+
void StoragePrivate::setError(StorageError *error, StorageErrorCode code, const QString &operation, const std::optional<QString> &key, const QString &message) const {
18+
if (!error) {
19+
return;
20+
}
21+
error->code = code;
22+
error->message = message;
23+
error->operation = operation;
24+
error->storageId = id;
25+
error->key = key;
26+
}
27+
28+
bool StoragePrivate::commit(const StorageItems &proposedItems, const QString &operation, const std::optional<QString> &key, StorageError *error) {
29+
if (error) {
30+
error->clear();
31+
}
32+
if (committing) {
33+
setError(error, StorageErrorCode::InvalidState, operation, key, Storage::tr("A storage commit callback cannot modify the same storage."));
34+
return false;
35+
}
36+
if (!commitCallback) {
37+
items = proposedItems;
38+
return true;
39+
}
40+
41+
StorageError localError;
42+
auto targetError = error ? error : &localError;
43+
QScopedValueRollback guard(committing, true);
44+
bool committed = false;
45+
try {
46+
committed = commitCallback(proposedItems, targetError);
47+
} catch (const std::exception &exception) {
48+
setError(targetError, StorageErrorCode::Unknown, operation, key, Storage::tr("The storage commit callback threw an exception: %1").arg(QString::fromUtf8(exception.what())));
49+
return false;
50+
} catch (...) {
51+
setError(targetError, StorageErrorCode::Unknown, operation, key, Storage::tr("The storage commit callback threw an unknown exception."));
52+
return false;
53+
}
54+
if (!committed) {
55+
if (!targetError->hasError()) {
56+
setError(targetError, StorageErrorCode::Unknown, operation, key, Storage::tr("The storage commit callback rejected the change."));
57+
} else {
58+
if (targetError->operation.isEmpty()) {
59+
targetError->operation = operation;
60+
}
61+
if (targetError->storageId.isEmpty()) {
62+
targetError->storageId = id;
63+
}
64+
if (!targetError->key && key) {
65+
targetError->key = key;
66+
}
67+
}
68+
return false;
69+
}
70+
71+
items = proposedItems;
72+
if (error) {
73+
error->clear();
74+
}
75+
return true;
76+
}
77+
78+
Storage::Storage(const QString &id, const StorageItems &initialItems, StorageCommitCallback commitCallback, QObject *parent)
79+
: QObject(parent), d_ptr(new StoragePrivate(this)) {
80+
Q_D(Storage);
81+
d->id = id;
82+
d->items = initialItems;
83+
d->commitCallback = std::move(commitCallback);
84+
}
85+
86+
Storage::~Storage() = default;
87+
88+
QString Storage::id() const {
89+
Q_D(const Storage);
90+
return d->id;
91+
}
92+
93+
qsizetype Storage::length() const {
94+
Q_D(const Storage);
95+
return d->items.size();
96+
}
97+
98+
QStringList Storage::keys() const {
99+
Q_D(const Storage);
100+
return d->items.keys();
101+
}
102+
103+
std::optional<QString> Storage::key(qsizetype index) const {
104+
Q_D(const Storage);
105+
if (index < 0 || index >= d->items.size()) {
106+
return std::nullopt;
107+
}
108+
return d->items.keys().at(index);
109+
}
110+
111+
std::optional<QString> Storage::getItem(const QString &key) const {
112+
Q_D(const Storage);
113+
const auto it = d->items.constFind(key);
114+
if (it == d->items.cend()) {
115+
return std::nullopt;
116+
}
117+
return it.value();
118+
}
119+
120+
StorageItems Storage::items() const {
121+
Q_D(const Storage);
122+
return d->items;
123+
}
124+
125+
bool Storage::setItem(const QString &key, const QString &value, StorageError *error) {
126+
Q_D(Storage);
127+
const auto it = d->items.constFind(key);
128+
if (it != d->items.cend() && it.value() == value) {
129+
if (error) {
130+
error->clear();
131+
}
132+
return true;
133+
}
134+
auto proposedItems = d->items;
135+
proposedItems.insert(key, value);
136+
return d->commit(proposedItems, QStringLiteral("setItem"), key, error);
137+
}
138+
139+
bool Storage::removeItem(const QString &key, StorageError *error) {
140+
Q_D(Storage);
141+
if (!d->items.contains(key)) {
142+
if (error) {
143+
error->clear();
144+
}
145+
return true;
146+
}
147+
auto proposedItems = d->items;
148+
proposedItems.remove(key);
149+
return d->commit(proposedItems, QStringLiteral("removeItem"), key, error);
150+
}
151+
152+
bool Storage::clear(StorageError *error) {
153+
Q_D(Storage);
154+
if (d->items.isEmpty()) {
155+
if (error) {
156+
error->clear();
157+
}
158+
return true;
159+
}
160+
return d->commit({}, QStringLiteral("clear"), std::nullopt, error);
161+
}
162+
163+
}
164+
165+
#include "moc_Storage.cpp"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-FileCopyrightText: Team OpenVPI
2+
// SPDX-License-Identifier: LGPL-3.0-or-later
3+
4+
#ifndef DIFFSCOPE_BATCHPROCESS_STORAGE_H
5+
#define DIFFSCOPE_BATCHPROCESS_STORAGE_H
6+
7+
#include <optional>
8+
9+
#include <QObject>
10+
#include <QScopedPointer>
11+
#include <QStringList>
12+
13+
#include <batchprocess/StorageTypes.h>
14+
#include <batchprocess/batchprocessglobal.h>
15+
16+
namespace BatchProcess {
17+
18+
class StorageInterface;
19+
class StoragePrivate;
20+
21+
class BATCH_PROCESS_EXPORT Storage : public QObject {
22+
Q_OBJECT
23+
Q_DECLARE_PRIVATE(Storage)
24+
public:
25+
~Storage() override;
26+
27+
QString id() const;
28+
qsizetype length() const;
29+
QStringList keys() const;
30+
std::optional<QString> key(qsizetype index) const;
31+
std::optional<QString> getItem(const QString &key) const;
32+
StorageItems items() const;
33+
34+
bool setItem(const QString &key, const QString &value, StorageError *error = nullptr);
35+
bool removeItem(const QString &key, StorageError *error = nullptr);
36+
bool clear(StorageError *error = nullptr);
37+
38+
private:
39+
friend class StorageInterface;
40+
41+
Storage(const QString &id, const StorageItems &initialItems, StorageCommitCallback commitCallback, QObject *parent = nullptr);
42+
43+
QScopedPointer<StoragePrivate> d_ptr;
44+
};
45+
46+
}
47+
48+
#endif // DIFFSCOPE_BATCHPROCESS_STORAGE_H
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// SPDX-FileCopyrightText: Team OpenVPI
2+
// SPDX-License-Identifier: LGPL-3.0-or-later
3+
4+
#include "StorageInterface.h"
5+
#include "StorageInterface_p.h"
6+
7+
#include <utility>
8+
9+
#include <QLoggingCategory>
10+
11+
#include <batchprocess/ScriptExecutionContext.h>
12+
#include <batchprocess/Storage.h>
13+
#include <batchprocess/internal/StorageModuleExtension.h>
14+
15+
namespace BatchProcess {
16+
17+
Q_STATIC_LOGGING_CATEGORY(lcStorageInterface, "diffscope.batchprocess.storage.interface")
18+
19+
namespace {
20+
21+
StorageInterface *s_instance{};
22+
23+
}
24+
25+
StorageInterfacePrivate::StorageInterfacePrivate(StorageInterface *q) : q_ptr(q) {
26+
}
27+
28+
void StorageInterfacePrivate::setError(StorageError *error, StorageErrorCode code, const QString &operation, const QString &storageId, const QString &message) {
29+
if (!error) {
30+
return;
31+
}
32+
error->code = code;
33+
error->message = message;
34+
error->operation = operation;
35+
error->storageId = storageId;
36+
error->key.reset();
37+
}
38+
39+
StorageInterface::StorageInterface(QObject *parent) : QObject(parent), d_ptr(new StorageInterfacePrivate(this)) {
40+
Q_ASSERT(!s_instance);
41+
s_instance = this;
42+
}
43+
44+
StorageInterface::~StorageInterface() {
45+
s_instance = nullptr;
46+
}
47+
48+
StorageInterface *StorageInterface::instance() {
49+
return s_instance;
50+
}
51+
52+
Storage *StorageInterface::createStorage(const QString &id, const StorageItems &initialItems, QObject *parent, StorageCommitCallback commitCallback) {
53+
return new Storage(id, initialItems, std::move(commitCallback), parent);
54+
}
55+
56+
QJSValue StorageInterface::toJavaScriptValue(ScriptExecutionContext *context, Storage *storage, StorageError *error) {
57+
Q_D(StorageInterface);
58+
if (!context || !context->engine() || !storage || !d->moduleExtension) {
59+
StorageInterfacePrivate::setError(error, StorageErrorCode::InvalidState, QStringLiteral("wrap"), storage ? storage->id() : QString(), tr("The storage cannot be wrapped in the current JavaScript execution context."));
60+
qCWarning(lcStorageInterface) << "Unable to wrap storage for JavaScript" << (storage ? storage->id() : QString());
61+
return QJSValue(QJSValue::UndefinedValue);
62+
}
63+
return d->moduleExtension->wrapStorage(context, storage, error);
64+
}
65+
66+
bool StorageInterface::fromJavaScriptValue(ScriptExecutionContext *context, const QJSValue &value, Storage **storage, StorageError *error) const {
67+
Q_D(const StorageInterface);
68+
if (!storage) {
69+
StorageInterfacePrivate::setError(error, StorageErrorCode::InvalidState, QStringLiteral("unwrap"), {}, tr("No storage output was provided."));
70+
return false;
71+
}
72+
*storage = nullptr;
73+
if (!context || !context->engine() || !d->moduleExtension) {
74+
StorageInterfacePrivate::setError(error, StorageErrorCode::InvalidState, QStringLiteral("unwrap"), {}, tr("The JavaScript execution context is not available."));
75+
qCWarning(lcStorageInterface) << "Unable to resolve JavaScript storage";
76+
return false;
77+
}
78+
return d->moduleExtension->unwrapStorage(context, value, storage, error);
79+
}
80+
81+
}
82+
83+
#include "moc_StorageInterface.cpp"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-FileCopyrightText: Team OpenVPI
2+
// SPDX-License-Identifier: LGPL-3.0-or-later
3+
4+
#ifndef DIFFSCOPE_BATCHPROCESS_STORAGEINTERFACE_H
5+
#define DIFFSCOPE_BATCHPROCESS_STORAGEINTERFACE_H
6+
7+
#include <QJSValue>
8+
#include <QObject>
9+
#include <QScopedPointer>
10+
11+
#include <batchprocess/StorageTypes.h>
12+
#include <batchprocess/batchprocessglobal.h>
13+
14+
namespace BatchProcess {
15+
16+
namespace Internal {
17+
class BatchProcessPlugin;
18+
class StorageModuleExtension;
19+
}
20+
21+
class ScriptExecutionContext;
22+
class Storage;
23+
class StorageInterfacePrivate;
24+
25+
class BATCH_PROCESS_EXPORT StorageInterface : public QObject {
26+
Q_OBJECT
27+
Q_DECLARE_PRIVATE(StorageInterface)
28+
public:
29+
~StorageInterface() override;
30+
31+
static StorageInterface *instance();
32+
33+
/** Creates an independently owned storage initialized with a snapshot of string values. */
34+
Storage *createStorage(const QString &id, const StorageItems &initialItems, QObject *parent, StorageCommitCallback commitCallback = {});
35+
36+
/** Returns the engine-specific frozen JavaScript wrapper for storage. */
37+
QJSValue toJavaScriptValue(ScriptExecutionContext *context, Storage *storage, StorageError *error = nullptr);
38+
/** Resolves a live Storage wrapped for the same JavaScript engine as context. */
39+
bool fromJavaScriptValue(ScriptExecutionContext *context, const QJSValue &value, Storage **storage, StorageError *error = nullptr) const;
40+
41+
private:
42+
friend class Internal::BatchProcessPlugin;
43+
friend class Internal::StorageModuleExtension;
44+
45+
explicit StorageInterface(QObject *parent = nullptr);
46+
47+
QScopedPointer<StorageInterfacePrivate> d_ptr;
48+
};
49+
50+
}
51+
52+
#endif // DIFFSCOPE_BATCHPROCESS_STORAGEINTERFACE_H
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-FileCopyrightText: Team OpenVPI
2+
// SPDX-License-Identifier: LGPL-3.0-or-later
3+
4+
#ifndef DIFFSCOPE_BATCHPROCESS_STORAGEINTERFACE_P_H
5+
#define DIFFSCOPE_BATCHPROCESS_STORAGEINTERFACE_P_H
6+
7+
#include <batchprocess/StorageInterface.h>
8+
9+
#include <QPointer>
10+
11+
namespace BatchProcess {
12+
13+
namespace Internal {
14+
class StorageModuleExtension;
15+
}
16+
17+
class StorageInterfacePrivate {
18+
Q_DECLARE_PUBLIC(StorageInterface)
19+
public:
20+
explicit StorageInterfacePrivate(StorageInterface *q);
21+
22+
static void setError(StorageError *error, StorageErrorCode code, const QString &operation, const QString &storageId, const QString &message);
23+
24+
StorageInterface *q_ptr;
25+
QPointer<Internal::StorageModuleExtension> moduleExtension;
26+
};
27+
28+
}
29+
30+
#endif // DIFFSCOPE_BATCHPROCESS_STORAGEINTERFACE_P_H

0 commit comments

Comments
 (0)