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