mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-09-02 10:41:15 +00:00
abstract provider side
This commit is contained in:
committed by
Logos Workspace
parent
d35653ec8a
commit
33bf0f94d3
@@ -12,6 +12,7 @@
|
||||
#include <QByteArrayList>
|
||||
#include <QFile>
|
||||
#include <QSet>
|
||||
#include <QRegularExpression>
|
||||
#include <QtGlobal>
|
||||
|
||||
static QJsonArray enumerateMethods(QObject* moduleInstance)
|
||||
@@ -741,6 +742,202 @@ static bool writeUmbrellaSourceFromDeps(const QString& genDirPath, const QJsonAr
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Provider-header mode: scan LOGOS_METHOD markers and generate dispatch ────
|
||||
|
||||
struct ParsedMethod {
|
||||
QString returnType;
|
||||
QString name;
|
||||
QVector<QPair<QString, QString>> params; // (type, name)
|
||||
};
|
||||
|
||||
static QVector<ParsedMethod> parseProviderHeader(const QString& headerPath, QTextStream& err)
|
||||
{
|
||||
QVector<ParsedMethod> methods;
|
||||
|
||||
QFile file(headerPath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
err << "Cannot open header file: " << headerPath << "\n";
|
||||
return methods;
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QRegularExpression re(
|
||||
R"(^\s*LOGOS_METHOD\s+(.+?)\s+(\w+)\s*\(([^)]*)\)\s*;)"
|
||||
);
|
||||
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine();
|
||||
auto match = re.match(line);
|
||||
if (!match.hasMatch()) continue;
|
||||
|
||||
ParsedMethod m;
|
||||
m.returnType = normalizeType(match.captured(1));
|
||||
m.name = match.captured(2);
|
||||
|
||||
QString paramStr = match.captured(3).trimmed();
|
||||
if (!paramStr.isEmpty()) {
|
||||
QStringList paramParts = paramStr.split(',');
|
||||
for (const QString& part : paramParts) {
|
||||
QString trimmed = part.trimmed();
|
||||
int lastSpace = trimmed.lastIndexOf(' ');
|
||||
int lastAmp = trimmed.lastIndexOf('&');
|
||||
int splitAt = qMax(lastSpace, lastAmp);
|
||||
if (splitAt > 0) {
|
||||
QString type = normalizeType(trimmed.left(splitAt + 1));
|
||||
QString pname = trimmed.mid(splitAt + 1).trimmed();
|
||||
m.params.append({type, pname});
|
||||
} else {
|
||||
m.params.append({normalizeType(trimmed), QString("arg%1").arg(m.params.size())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
methods.append(m);
|
||||
}
|
||||
|
||||
file.close();
|
||||
return methods;
|
||||
}
|
||||
|
||||
static QString toQVariantConversion(const QString& type, const QString& argExpr)
|
||||
{
|
||||
if (type == "int") return argExpr + ".toInt()";
|
||||
if (type == "bool") return argExpr + ".toBool()";
|
||||
if (type == "double") return argExpr + ".toDouble()";
|
||||
if (type == "float") return argExpr + ".toFloat()";
|
||||
if (type == "QString") return argExpr + ".toString()";
|
||||
if (type == "QStringList") return argExpr + ".toStringList()";
|
||||
if (type == "QJsonArray") return "qvariant_cast<QJsonArray>(" + argExpr + ")";
|
||||
if (type == "QVariant") return argExpr;
|
||||
if (type == "LogosResult") return argExpr + ".value<LogosResult>()";
|
||||
return argExpr + ".toString()";
|
||||
}
|
||||
|
||||
static int generateProviderDispatch(const QString& headerPath, const QString& outputDir, QTextStream& out, QTextStream& err)
|
||||
{
|
||||
QFileInfo fi(headerPath);
|
||||
if (!fi.exists()) {
|
||||
err << "Header file does not exist: " << headerPath << "\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
QVector<ParsedMethod> methods = parseProviderHeader(headerPath, err);
|
||||
if (methods.isEmpty()) {
|
||||
err << "No LOGOS_METHOD markers found in: " << headerPath << "\n";
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Derive the class name from the header: parse for ": public LogosProviderBase"
|
||||
QString className;
|
||||
{
|
||||
QFile f(headerPath);
|
||||
f.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
QTextStream ts(&f);
|
||||
QRegularExpression classRe(R"(class\s+(\w+)\s*:\s*public\s+LogosProviderBase)");
|
||||
while (!ts.atEnd()) {
|
||||
QString line = ts.readLine();
|
||||
auto m = classRe.match(line);
|
||||
if (m.hasMatch()) {
|
||||
className = m.captured(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
if (className.isEmpty()) {
|
||||
err << "Could not find class inheriting LogosProviderBase in: " << headerPath << "\n";
|
||||
return 4;
|
||||
}
|
||||
|
||||
QString headerBaseName = fi.fileName();
|
||||
|
||||
QString genDirPath = outputDir.isEmpty() ? fi.absolutePath() : outputDir;
|
||||
QDir().mkpath(genDirPath);
|
||||
|
||||
// Generate logos_provider_dispatch.cpp
|
||||
QString content;
|
||||
QTextStream s(&content);
|
||||
|
||||
s << "// AUTO-GENERATED by logos-cpp-generator -- do not edit\n";
|
||||
s << "#include \"" << headerBaseName << "\"\n";
|
||||
s << "#include <QJsonArray>\n";
|
||||
s << "#include <QJsonObject>\n";
|
||||
s << "#include <QVariant>\n";
|
||||
s << "#include <QString>\n";
|
||||
s << "#include \"logos_types.h\"\n\n";
|
||||
|
||||
// callMethod()
|
||||
s << "QVariant " << className << "::callMethod(const QString& methodName, const QVariantList& args)\n";
|
||||
s << "{\n";
|
||||
for (const ParsedMethod& m : methods) {
|
||||
s << " if (methodName == \"" << m.name << "\") {\n";
|
||||
if (m.returnType == "void" || m.returnType.isEmpty()) {
|
||||
s << " " << m.name << "(";
|
||||
for (int i = 0; i < m.params.size(); ++i) {
|
||||
s << toQVariantConversion(m.params[i].first, QString("args.at(%1)").arg(i));
|
||||
if (i + 1 < m.params.size()) s << ", ";
|
||||
}
|
||||
s << ");\n";
|
||||
s << " return QVariant(true);\n";
|
||||
} else {
|
||||
s << " return QVariant::fromValue(" << m.name << "(";
|
||||
for (int i = 0; i < m.params.size(); ++i) {
|
||||
s << toQVariantConversion(m.params[i].first, QString("args.at(%1)").arg(i));
|
||||
if (i + 1 < m.params.size()) s << ", ";
|
||||
}
|
||||
s << "));\n";
|
||||
}
|
||||
s << " }\n";
|
||||
}
|
||||
s << " qWarning() << \"" << className << "::callMethod: unknown method:\" << methodName;\n";
|
||||
s << " return QVariant();\n";
|
||||
s << "}\n\n";
|
||||
|
||||
// getMethods()
|
||||
s << "QJsonArray " << className << "::getMethods()\n";
|
||||
s << "{\n";
|
||||
s << " QJsonArray methods;\n";
|
||||
for (const ParsedMethod& m : methods) {
|
||||
s << " {\n";
|
||||
s << " QJsonObject obj;\n";
|
||||
s << " obj[\"name\"] = QStringLiteral(\"" << m.name << "\");\n";
|
||||
s << " obj[\"returnType\"] = QStringLiteral(\"" << m.returnType << "\");\n";
|
||||
s << " obj[\"isInvokable\"] = true;\n";
|
||||
QString sig = m.name + "(";
|
||||
for (int i = 0; i < m.params.size(); ++i) {
|
||||
sig += m.params[i].first;
|
||||
if (i + 1 < m.params.size()) sig += ",";
|
||||
}
|
||||
sig += ")";
|
||||
s << " obj[\"signature\"] = QStringLiteral(\"" << sig << "\");\n";
|
||||
if (!m.params.isEmpty()) {
|
||||
s << " QJsonArray params;\n";
|
||||
for (int i = 0; i < m.params.size(); ++i) {
|
||||
s << " params.append(QJsonObject{{\"type\", QStringLiteral(\"" << m.params[i].first << "\")}, {\"name\", QStringLiteral(\"" << m.params[i].second << "\")}});\n";
|
||||
}
|
||||
s << " obj[\"parameters\"] = params;\n";
|
||||
}
|
||||
s << " methods.append(obj);\n";
|
||||
s << " }\n";
|
||||
}
|
||||
s << " return methods;\n";
|
||||
s << "}\n";
|
||||
|
||||
QString outputPath = QDir(genDirPath).filePath("logos_provider_dispatch.cpp");
|
||||
QFile outFile(outputPath);
|
||||
if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||
err << "Failed to write dispatch file: " << outputPath << "\n";
|
||||
return 5;
|
||||
}
|
||||
outFile.write(content.toUtf8());
|
||||
outFile.close();
|
||||
|
||||
out << "Generated provider dispatch: " << outputPath << " (" << methods.size() << " methods from " << className << ")\n";
|
||||
out.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generateFromPlugin(const QString& pluginInputPath, const QString& outputDir, bool moduleOnly, QTextStream& out, QTextStream& err)
|
||||
{
|
||||
QFileInfo fi(pluginInputPath);
|
||||
@@ -989,10 +1186,25 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// --provider-header mode: scan LOGOS_METHOD markers and generate dispatch code
|
||||
{
|
||||
const int phIdx = args.indexOf("--provider-header");
|
||||
if (phIdx != -1) {
|
||||
if (phIdx + 1 >= args.size()) {
|
||||
err << "Usage: " << QFileInfo(app.applicationFilePath()).fileName() << " --provider-header /path/to/impl.h [--output-dir /path/to/output]\n";
|
||||
return 1;
|
||||
}
|
||||
QString headerArg = args.at(phIdx + 1);
|
||||
if (headerArg.startsWith('@')) headerArg.remove(0, 1);
|
||||
return generateProviderDispatch(headerArg, outputDir, out, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.size() < 2) {
|
||||
err << "Usage: " << QFileInfo(app.applicationFilePath()).fileName() << " /absolute/path/to/plugin [--output-dir /path/to/output] [--module-only]\n";
|
||||
err << " or: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /absolute/path/to/metadata.json [--output-dir /path/to/output] [--module-only] [--general-only]\n";
|
||||
err << " or: " << QFileInfo(app.applicationFilePath()).fileName() << " --metadata /absolute/path/to/metadata.json --general-only [--output-dir /path/to/output]\n";
|
||||
err << " or: " << QFileInfo(app.applicationFilePath()).fileName() << " --provider-header /path/to/impl.h [--output-dir /path/to/output]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user