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"))
62 return QMetaType::QString;
63 if (str == QLatin1String("double"))
64 return QMetaType::Double;
65 if (str == QLatin1String("float"))
66 return QMetaType::Float;
67 if (str == QLatin1String("int"))
68 return QMetaType::Int;
69 if (str == QLatin1String("bool"))
70 return QMetaType::Bool;
71 if (str == QLatin1String("path"))
72 return QMetaType::QString;
73 return QMetaType::QString;
74}
75
76//=============================================================================================================
77
78static QString metaTypeToString(QMetaType::Type type)
79{
80 switch (type) {
81 case QMetaType::QString:
82 return QStringLiteral("string");
83 case QMetaType::Double:
84 return QStringLiteral("double");
85 case QMetaType::Float:
86 return QStringLiteral("float");
87 case QMetaType::Int:
88 return QStringLiteral("int");
89 case QMetaType::Bool:
90 return QStringLiteral("bool");
91 default:
92 return QStringLiteral("string");
93 }
94}
95
96//=============================================================================================================
97
98static MnaOpSchemaPort parsePort(const QJsonObject& obj)
99{
100 MnaOpSchemaPort port;
101 port.name = obj.value(QLatin1String("name")).toString();
102 port.dataKind = mnaDataKindFromString(obj.value(QLatin1String("kind")).toString());
103 port.required = obj.value(QLatin1String("required")).toBool(true);
104 port.description = obj.value(QLatin1String("description")).toString();
105 return port;
106}
107
108//=============================================================================================================
109
110static QJsonObject portToJson(const MnaOpSchemaPort& port)
111{
112 QJsonObject obj;
113 obj[QLatin1String("name")] = port.name;
114 obj[QLatin1String("kind")] = mnaDataKindToString(port.dataKind);
115 obj[QLatin1String("description")] = port.description;
116 if (!port.required)
117 obj[QLatin1String("required")] = false;
118 return obj;
119}
120
121//=============================================================================================================
122
123static MnaOpSchemaAttr parseAttr(const QJsonObject& obj)
124{
125 MnaOpSchemaAttr attr;
126 attr.name = obj.value(QLatin1String("name")).toString();
127 attr.type = metaTypeFromString(obj.value(QLatin1String("type")).toString());
128 attr.required = obj.value(QLatin1String("required")).toBool(false);
129 attr.description = obj.value(QLatin1String("description")).toString();
130 if (obj.contains(QLatin1String("default")))
131 attr.defaultValue = obj.value(QLatin1String("default")).toVariant();
132 return attr;
133}
134
135//=============================================================================================================
136
137static QJsonObject attrToJson(const MnaOpSchemaAttr& attr)
138{
139 QJsonObject obj;
140 obj[QLatin1String("name")] = attr.name;
141 obj[QLatin1String("type")] = metaTypeToString(attr.type);
142 if (attr.required)
143 obj[QLatin1String("required")] = true;
144 if (!attr.description.isEmpty())
145 obj[QLatin1String("description")] = attr.description;
146 if (attr.defaultValue.isValid())
147 obj[QLatin1String("default")] = QJsonValue::fromVariant(attr.defaultValue);
148 return obj;
149}
150
151//=============================================================================================================
152
153static MnaOpSchema parseOp(const QJsonObject& obj)
154{
155 MnaOpSchema schema;
156 schema.opType = obj.value(QLatin1String("type")).toString();
157 schema.version = obj.value(QLatin1String("version")).toString();
158 schema.binding = obj.value(QLatin1String("binding")).toString(QStringLiteral("internal"));
159 schema.category = obj.value(QLatin1String("category")).toString();
160 schema.description = obj.value(QLatin1String("description")).toString();
161 schema.library = obj.value(QLatin1String("library")).toString();
162 schema.executable = obj.value(QLatin1String("executable")).toString();
163 schema.cliTemplate = obj.value(QLatin1String("cli_template")).toString();
164
165 const QJsonArray inputs = obj.value(QLatin1String("inputs")).toArray();
166 for (const QJsonValue& v : inputs)
167 schema.inputPorts.append(parsePort(v.toObject()));
168
169 const QJsonArray outputs = obj.value(QLatin1String("outputs")).toArray();
170 for (const QJsonValue& v : outputs)
171 schema.outputPorts.append(parsePort(v.toObject()));
172
173 const QJsonArray params = obj.value(QLatin1String("parameters")).toArray();
174 for (const QJsonValue& v : params)
175 schema.attributes.append(parseAttr(v.toObject()));
176
177 return schema;
178}
179
180//=============================================================================================================
181
182static QJsonObject opToJson(const MnaOpSchema& schema)
183{
184 QJsonObject obj;
185 obj[QLatin1String("type")] = schema.opType;
186 obj[QLatin1String("description")] = schema.description;
187
188 if (!schema.version.isEmpty())
189 obj[QLatin1String("version")] = schema.version;
190 if (!schema.binding.isEmpty())
191 obj[QLatin1String("binding")] = schema.binding;
192 if (!schema.category.isEmpty())
193 obj[QLatin1String("category")] = schema.category;
194 if (!schema.library.isEmpty())
195 obj[QLatin1String("library")] = schema.library;
196 if (!schema.executable.isEmpty())
197 obj[QLatin1String("executable")] = schema.executable;
198 if (!schema.cliTemplate.isEmpty())
199 obj[QLatin1String("cli_template")] = schema.cliTemplate;
200
201 QJsonArray inputs;
202 for (const MnaOpSchemaPort& p : schema.inputPorts)
203 inputs.append(portToJson(p));
204 obj[QLatin1String("inputs")] = inputs;
205
206 QJsonArray outputs;
207 for (const MnaOpSchemaPort& p : schema.outputPorts)
208 outputs.append(portToJson(p));
209 obj[QLatin1String("outputs")] = outputs;
210
211 QJsonArray params;
212 for (const MnaOpSchemaAttr& a : schema.attributes)
213 params.append(attrToJson(a));
214 obj[QLatin1String("parameters")] = params;
215
216 return obj;
217}
218
219//=============================================================================================================
220// DEFINE MEMBER METHODS
221//=============================================================================================================
222
223int MnaRegistryLoader::loadFile(const QString& path, MnaOpRegistry& registry)
224{
225 QFile file(path);
226 if (!file.open(QIODevice::ReadOnly)) {
227 qWarning() << "MnaRegistryLoader: Cannot open" << path;
228 return -1;
229 }
230
231 QJsonParseError parseError;
232 QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError);
233 file.close();
234
235 if (parseError.error != QJsonParseError::NoError) {
236 qWarning() << "MnaRegistryLoader: JSON parse error in" << path
237 << ":" << parseError.errorString();
238 return -1;
239 }
240
241 const QJsonObject root = doc.object();
242
243 // Validate required fields
244 if (!root.contains(QLatin1String("mna_registry_version"))) {
245 qWarning() << "MnaRegistryLoader: Missing 'mna_registry_version' in" << path;
246 return -1;
247 }
248
249 const QJsonArray ops = root.value(QLatin1String("ops")).toArray();
250 int count = 0;
251
252 for (const QJsonValue& v : ops) {
253 MnaOpSchema schema = parseOp(v.toObject());
254 if (!schema.opType.isEmpty()) {
255 registry.registerOp(schema);
256 ++count;
257 }
258 }
259
260 qInfo() << "MnaRegistryLoader: Loaded" << count << "ops from" << path
261 << "(provider:" << root.value(QLatin1String("provider")).toString() << ")";
262
263 return count;
264}
265
266//=============================================================================================================
267
268int MnaRegistryLoader::loadDirectory(const QString& registryDir, MnaOpRegistry& registry)
269{
270 int total = 0;
271
272 // 1. Load master manifest
273 QString masterPath = registryDir + QStringLiteral("/mna-registry.json");
274 if (QFile::exists(masterPath)) {
275 int n = loadFile(masterPath, registry);
276 if (n > 0)
277 total += n;
278 }
279
280 // 2. Load drop-in files from mna-registry.d/
281 QDir dropInDir(registryDir + QStringLiteral("/mna-registry.d"));
282 if (dropInDir.exists()) {
283 QStringList filters;
284 filters << QStringLiteral("*.json");
285 QStringList files = dropInDir.entryList(filters, QDir::Files, QDir::Name);
286 for (const QString& fileName : files) {
287 int n = loadFile(dropInDir.absoluteFilePath(fileName), registry);
288 if (n > 0)
289 total += n;
290 }
291 }
292
293 return total;
294}
295
296//=============================================================================================================
297
298bool MnaRegistryLoader::saveFile(const QString& path,
299 const QString& provider,
300 const MnaOpRegistry& registry)
301{
302 QJsonObject root;
303 root[QLatin1String("mna_registry_version")] = QStringLiteral("1.0");
304 root[QLatin1String("provider")] = provider;
305
306 QJsonArray ops;
307 const QStringList opTypes = registry.registeredOps();
308 for (const QString& opType : opTypes)
309 ops.append(opToJson(registry.schema(opType)));
310 root[QLatin1String("ops")] = ops;
311
312 QFile file(path);
313 if (!file.open(QIODevice::WriteOnly)) {
314 qWarning() << "MnaRegistryLoader: Cannot write" << path;
315 return false;
316 }
317
318 file.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
319 file.close();
320 return true;
321}
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 MNALIB::MnaOpRegistry contents — ingests mna-registry.json manifests and overr...
Process-wide singleton catalog mapping opType strings to their MNALIB::MnaOpSchema and (for built-in ...
MNE Analysis Container Format (mna/mnx).
QString mnaDataKindToString(MnaDataKind kind)
Definition mna_types.h:234
MnaDataKind mnaDataKindFromString(const QString &str)
Definition mna_types.h:280
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)