v2.0.0
Loading...
Searching...
No Matches
mna_registry_loader.cpp
Go to the documentation of this file.
1//=============================================================================================================
28
29//=============================================================================================================
30// INCLUDES
31//=============================================================================================================
32
33#include "mna_registry_loader.h"
34#include "mna_op_registry.h"
35#include "mna_op_schema.h"
36#include "mna_types.h"
37
38//=============================================================================================================
39// QT INCLUDES
40//=============================================================================================================
41
42#include <QFile>
43#include <QDir>
44#include <QJsonDocument>
45#include <QJsonObject>
46#include <QJsonArray>
47#include <QDebug>
48
49//=============================================================================================================
50// USED NAMESPACES
51//=============================================================================================================
52
53using namespace MNALIB;
54
55//=============================================================================================================
56// STATIC HELPERS
57//=============================================================================================================
58
59static QMetaType::Type metaTypeFromString(const QString& str)
60{
61 if (str == QLatin1String("string")) return QMetaType::QString;
62 if (str == QLatin1String("double")) return QMetaType::Double;
63 if (str == QLatin1String("float")) return QMetaType::Float;
64 if (str == QLatin1String("int")) return QMetaType::Int;
65 if (str == QLatin1String("bool")) return QMetaType::Bool;
66 if (str == QLatin1String("path")) return QMetaType::QString;
67 return QMetaType::QString;
68}
69
70//=============================================================================================================
71
72static QString metaTypeToString(QMetaType::Type type)
73{
74 switch (type) {
75 case QMetaType::QString: return QStringLiteral("string");
76 case QMetaType::Double: return QStringLiteral("double");
77 case QMetaType::Float: return QStringLiteral("float");
78 case QMetaType::Int: return QStringLiteral("int");
79 case QMetaType::Bool: return QStringLiteral("bool");
80 default: return QStringLiteral("string");
81 }
82}
83
84//=============================================================================================================
85
86static MnaOpSchemaPort parsePort(const QJsonObject& obj)
87{
88 MnaOpSchemaPort port;
89 port.name = obj.value(QLatin1String("name")).toString();
90 port.dataKind = mnaDataKindFromString(obj.value(QLatin1String("kind")).toString());
91 port.required = obj.value(QLatin1String("required")).toBool(true);
92 port.description = obj.value(QLatin1String("description")).toString();
93 return port;
94}
95
96//=============================================================================================================
97
98static QJsonObject portToJson(const MnaOpSchemaPort& port)
99{
100 QJsonObject obj;
101 obj[QLatin1String("name")] = port.name;
102 obj[QLatin1String("kind")] = mnaDataKindToString(port.dataKind);
103 obj[QLatin1String("description")] = port.description;
104 if (!port.required)
105 obj[QLatin1String("required")] = false;
106 return obj;
107}
108
109//=============================================================================================================
110
111static MnaOpSchemaAttr parseAttr(const QJsonObject& obj)
112{
113 MnaOpSchemaAttr attr;
114 attr.name = obj.value(QLatin1String("name")).toString();
115 attr.type = metaTypeFromString(obj.value(QLatin1String("type")).toString());
116 attr.required = obj.value(QLatin1String("required")).toBool(false);
117 attr.description = obj.value(QLatin1String("description")).toString();
118 if (obj.contains(QLatin1String("default")))
119 attr.defaultValue = obj.value(QLatin1String("default")).toVariant();
120 return attr;
121}
122
123//=============================================================================================================
124
125static QJsonObject attrToJson(const MnaOpSchemaAttr& attr)
126{
127 QJsonObject obj;
128 obj[QLatin1String("name")] = attr.name;
129 obj[QLatin1String("type")] = metaTypeToString(attr.type);
130 if (attr.required)
131 obj[QLatin1String("required")] = true;
132 if (!attr.description.isEmpty())
133 obj[QLatin1String("description")] = attr.description;
134 if (attr.defaultValue.isValid())
135 obj[QLatin1String("default")] = QJsonValue::fromVariant(attr.defaultValue);
136 return obj;
137}
138
139//=============================================================================================================
140
141static MnaOpSchema parseOp(const QJsonObject& obj)
142{
143 MnaOpSchema schema;
144 schema.opType = obj.value(QLatin1String("type")).toString();
145 schema.version = obj.value(QLatin1String("version")).toString();
146 schema.binding = obj.value(QLatin1String("binding")).toString(QStringLiteral("internal"));
147 schema.category = obj.value(QLatin1String("category")).toString();
148 schema.description = obj.value(QLatin1String("description")).toString();
149 schema.library = obj.value(QLatin1String("library")).toString();
150 schema.executable = obj.value(QLatin1String("executable")).toString();
151 schema.cliTemplate = obj.value(QLatin1String("cli_template")).toString();
152
153 const QJsonArray inputs = obj.value(QLatin1String("inputs")).toArray();
154 for (const QJsonValue& v : inputs)
155 schema.inputPorts.append(parsePort(v.toObject()));
156
157 const QJsonArray outputs = obj.value(QLatin1String("outputs")).toArray();
158 for (const QJsonValue& v : outputs)
159 schema.outputPorts.append(parsePort(v.toObject()));
160
161 const QJsonArray params = obj.value(QLatin1String("parameters")).toArray();
162 for (const QJsonValue& v : params)
163 schema.attributes.append(parseAttr(v.toObject()));
164
165 return schema;
166}
167
168//=============================================================================================================
169
170static QJsonObject opToJson(const MnaOpSchema& schema)
171{
172 QJsonObject obj;
173 obj[QLatin1String("type")] = schema.opType;
174 obj[QLatin1String("description")] = schema.description;
175
176 if (!schema.version.isEmpty())
177 obj[QLatin1String("version")] = schema.version;
178 if (!schema.binding.isEmpty())
179 obj[QLatin1String("binding")] = schema.binding;
180 if (!schema.category.isEmpty())
181 obj[QLatin1String("category")] = schema.category;
182 if (!schema.library.isEmpty())
183 obj[QLatin1String("library")] = schema.library;
184 if (!schema.executable.isEmpty())
185 obj[QLatin1String("executable")] = schema.executable;
186 if (!schema.cliTemplate.isEmpty())
187 obj[QLatin1String("cli_template")] = schema.cliTemplate;
188
189 QJsonArray inputs;
190 for (const MnaOpSchemaPort& p : schema.inputPorts)
191 inputs.append(portToJson(p));
192 obj[QLatin1String("inputs")] = inputs;
193
194 QJsonArray outputs;
195 for (const MnaOpSchemaPort& p : schema.outputPorts)
196 outputs.append(portToJson(p));
197 obj[QLatin1String("outputs")] = outputs;
198
199 QJsonArray params;
200 for (const MnaOpSchemaAttr& a : schema.attributes)
201 params.append(attrToJson(a));
202 obj[QLatin1String("parameters")] = params;
203
204 return obj;
205}
206
207//=============================================================================================================
208// DEFINE MEMBER METHODS
209//=============================================================================================================
210
211int MnaRegistryLoader::loadFile(const QString& path, MnaOpRegistry& registry)
212{
213 QFile file(path);
214 if (!file.open(QIODevice::ReadOnly)) {
215 qWarning() << "MnaRegistryLoader: Cannot open" << path;
216 return -1;
217 }
218
219 QJsonParseError parseError;
220 QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError);
221 file.close();
222
223 if (parseError.error != QJsonParseError::NoError) {
224 qWarning() << "MnaRegistryLoader: JSON parse error in" << path
225 << ":" << parseError.errorString();
226 return -1;
227 }
228
229 const QJsonObject root = doc.object();
230
231 // Validate required fields
232 if (!root.contains(QLatin1String("mna_registry_version"))) {
233 qWarning() << "MnaRegistryLoader: Missing 'mna_registry_version' in" << path;
234 return -1;
235 }
236
237 const QJsonArray ops = root.value(QLatin1String("ops")).toArray();
238 int count = 0;
239
240 for (const QJsonValue& v : ops) {
241 MnaOpSchema schema = parseOp(v.toObject());
242 if (!schema.opType.isEmpty()) {
243 registry.registerOp(schema);
244 ++count;
245 }
246 }
247
248 qInfo() << "MnaRegistryLoader: Loaded" << count << "ops from" << path
249 << "(provider:" << root.value(QLatin1String("provider")).toString() << ")";
250
251 return count;
252}
253
254//=============================================================================================================
255
256int MnaRegistryLoader::loadDirectory(const QString& registryDir, MnaOpRegistry& registry)
257{
258 int total = 0;
259
260 // 1. Load master manifest
261 QString masterPath = registryDir + QStringLiteral("/mna-registry.json");
262 if (QFile::exists(masterPath)) {
263 int n = loadFile(masterPath, registry);
264 if (n > 0) total += n;
265 }
266
267 // 2. Load drop-in files from mna-registry.d/
268 QDir dropInDir(registryDir + QStringLiteral("/mna-registry.d"));
269 if (dropInDir.exists()) {
270 QStringList filters;
271 filters << QStringLiteral("*.json");
272 QStringList files = dropInDir.entryList(filters, QDir::Files, QDir::Name);
273 for (const QString& fileName : files) {
274 int n = loadFile(dropInDir.absoluteFilePath(fileName), registry);
275 if (n > 0) total += n;
276 }
277 }
278
279 return total;
280}
281
282//=============================================================================================================
283
284bool MnaRegistryLoader::saveFile(const QString& path,
285 const QString& provider,
286 const MnaOpRegistry& registry)
287{
288 QJsonObject root;
289 root[QLatin1String("mna_registry_version")] = QStringLiteral("1.0");
290 root[QLatin1String("provider")] = provider;
291
292 QJsonArray ops;
293 const QStringList opTypes = registry.registeredOps();
294 for (const QString& opType : opTypes)
295 ops.append(opToJson(registry.schema(opType)));
296 root[QLatin1String("ops")] = ops;
297
298 QFile file(path);
299 if (!file.open(QIODevice::WriteOnly)) {
300 qWarning() << "MnaRegistryLoader: Cannot write" << path;
301 return false;
302 }
303
304 file.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
305 file.close();
306 return true;
307}
Declarative contract for an MNA operation — expected input/output ports, attributes,...
Enums and string-conversion helpers for the MNA container vocabulary (file roles, data kinds,...
Declarative loader for MnaOpRegistry contents — ingests mna-registry.json manifests and overrides the...
Process-wide singleton catalog mapping opType strings to their MnaOpSchema and (for built-in ops) exe...
MNE Analysis Container Format (mna/mnx).
QString mnaDataKindToString(MnaDataKind kind)
Definition mna_types.h:186
MnaDataKind mnaDataKindFromString(const QString &str)
Definition mna_types.h:213
Process-wide lookup from opType to MnaOpSchema and implementation function.
QStringList registeredOps() const
void registerOp(const MnaOpSchema &schema)
MnaOpSchema schema(const QString &opType) const
QString name
Port name.
bool required
Must be connected?
QString description
Human-readable description.
MnaDataKind dataKind
Expected data kind.
QVariant defaultValue
Default when not set.
QMetaType::Type type
Expected value type.
QString description
Human-readable description.
QString name
Attribute key.
bool required
Must be set?
Operation schema for graph validation.
QList< MnaOpSchemaPort > outputPorts
Expected output ports.
QString binding
Binding type: "internal", "cli", or "script".
QList< MnaOpSchemaPort > inputPorts
Expected input ports.
QList< MnaOpSchemaAttr > attributes
Expected attributes.
QString category
Category: "io", "preprocessing", "source_estimation", etc.
QString opType
Operation type string.
QString version
Version of the operation (e.g. "2.2.0").
QString executable
For CLI ops: executable name (e.g. "recon-all").
QString library
Which library provides the implementation.
QString description
What the operation does.
QString cliTemplate
For CLI ops: command template with {{placeholder}} tokens.
static bool saveFile(const QString &path, const QString &provider, const MnaOpRegistry &registry)
static int loadDirectory(const QString &registryDir, MnaOpRegistry &registry)
static int loadFile(const QString &path, MnaOpRegistry &registry)