Files
logos-plugin-qt/cpp/logos_api_provider.h
T
Dario Gabriel LipicarandClaude Opus 5 c2c9137025 feat: the Qt host runtime and the cdylib-glue generator
This repo was the Qt plugin BUILD backend — pure Nix functions plus a CMake
module, with no C++ target at all. But the two things a Qt host actually needs
at runtime lived in logos-qt-sdk, where they are neither a language wrapper over
the protocol C API nor codegen:

  LogosAPI / LogosAPIProvider   owns a LogosTransportHost per transport,
                                constructs ModuleProxy / ModuleHandshakeProxy,
                                publishes the handshake surface, seeds trust
                                anchors, injects the token validator
  LogosProviderBase             the base every generated provider derives
  PluginInterface               the Qt plugin loading contract
  the cdylib->Qt glue generator the emitter that wraps a language-neutral
                                cdylib in a Qt plugin

They move here, so "swap the plugin technology" is a one-repo change.

ADDITIVE: the sources are COPIED and logos-qt-sdk is untouched. Removing them
there now would turn nine downstream masters red at once; that comes later,
after consumers are repointed.

qt_provider_object (and logos_qt_arg_decode, which its QMetaObject dispatch
needs) is carried deliberately even though it is legacy: logos_api_provider
falls back to wrapping a plain QObject in it, and the modules that rely on that
have not been migrated yet. It goes once they are.

The generator links Qt Core and logos-lidl only — never logos-cpp-sdk. It needed
exactly one helper from that SDK's shared frontend, lidlToPascalCase (~12
lines), which is inlined instead, the same way logos-view-module does it. It
also REFUSES `--backend <anything but cdylib>` rather than ignoring the flag:
callers are migrating from a tool where --backend was required and dispatched
on, so silently treating `--backend qt` as cdylib would emit confidently wrong
artifacts with a zero exit.

`rawLib`, `lib` and `cmake-module` deliberately do not reference the new
derivations, so a consumer that only wants the Nix build functions never
realises a Qt/protocol build. Proven, not assumed: with both new inputs
overridden to a bogus flake, cmake-module and rawLib still evaluate while
logos-qt-host fails — so the override bites and the cheap outputs really never
touch it.

Behaviour preservation is the whole claim of a relocation, so it is measured:
the emitted glue is BYTE-IDENTICAL to logos-qt-generator --backend cdylib over
every one of the 20 .lidl contracts in the workspace, in both single and
concurrency:multi mode (40 pairs, exit codes included), and single vs multi do
differ from each other, so both code paths were really exercised. The built
liblogos_qt_host.a is byte-identical to liblogos_qt_sdk.a, exporting the same
390 symbols.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:04:50 -03:00

124 lines
5.1 KiB
C++

#ifndef LOGOS_API_PROVIDER_H
#define LOGOS_API_PROVIDER_H
#include "logos_transport_config.h"
#include <QObject>
#include <QString>
#include <QVariant>
#include <QVariantList>
#include <QMap>
#include <functional>
#include <memory>
#include <string>
class LogosTransportHost;
class LogosObject;
class ModuleProxy;
class ModuleHandshakeProxy;
class LogosProviderObject;
class QtProviderObject;
/**
* @brief LogosAPIProvider handles registering objects for access by consumers
*
* Supports two registration paths:
* 1. registerObject(name, QObject*) — wraps in QtProviderObject, then ModuleProxy
* 2. registerObject(name, LogosProviderObject*) — wraps directly in ModuleProxy
* Both paths converge at ModuleProxy -> transport.
*/
class LogosAPIProvider : public QObject
{
Q_OBJECT
public:
/**
* @param module_name The module this provider belongs to.
* @param transports Optional per-instance transport override. When empty,
* the provider uses the process-global default. This
* is what lets a daemon expose `core_service` on
* TCP/TLS while keeping module-to-module traffic on
* the local-socket default.
*/
explicit LogosAPIProvider(const QString& module_name,
LogosTransportSet transports = {},
QObject *parent = nullptr);
// Back-compat overload
LogosAPIProvider(const QString& module_name, QObject *parent)
: LogosAPIProvider(module_name, LogosTransportSet{}, parent) {}
~LogosAPIProvider();
/**
* @brief Register a legacy QObject-based plugin.
* Wraps in QtProviderObject, then ModuleProxy.
*/
bool registerObject(const QString& name, QObject* object);
/**
* @brief Register a legacy QObject-based plugin — const char* overload (resolves ambiguity)
*/
bool registerObject(const char* name, QObject* object)
{ return registerObject(QString(name), object); }
/**
* @brief Register a legacy QObject-based plugin (std::string overload).
*/
bool registerObject(const std::string& name, QObject* object);
/**
* @brief Register a new-API LogosProviderObject plugin.
* Wraps directly in ModuleProxy.
*/
bool registerObject(const QString& name, LogosProviderObject* provider);
QString registryUrl() const;
bool saveToken(const QString& from_module_name, const QString& token);
// Install an extra token authorizer, forwarded to the ModuleProxy. Consulted
// in addition to the built-in issued-token scan, with the call's transport
// ("local" | "tcp" | "tcp_ssl") so local_only tokens can be enforced. The
// daemon backs this with TokenStore::lookupByToken so operator-issued named
// tokens authorize. Safe to call before or after registerObject(); a
// validator set before registration is applied when the proxy is created.
using TokenValidator = std::function<bool(const QString& token,
const QString& transportProtocol)>;
void setTokenValidator(TokenValidator validator);
public slots:
void onEventResponse(LogosObject* object, const QString& eventName, const QVariantList& data);
private:
bool publishProvider(const QString& name, LogosProviderObject* provider);
// Publishes the token-only handshake surface (logos::handshakeObjectName)
// before the module's initializer runs, so capability_module can deliver a
// token to a module that is still starting up. Best-effort: a transport that
// declines it simply leaves capability_module falling back to the business
// object, which is the pre-existing behaviour.
void publishHandshake(const QString& name, LogosProviderObject* provider);
// Installs the host-issued token as this module's "core"/"capability_module"
// trust anchor before the handshake surface is published. Without it the
// surface is reachable but refuses every push for the whole pre-init window
// it exists to cover, because the initializer that normally seeds those
// entries has not run yet. Never overwrites an existing entry, and is a no-op
// on a host that does not publish an `authToken` property.
void seedHandshakeTrustAnchor();
// One host per configured transport. Single-entry vector for back-compat.
std::vector<std::unique_ptr<LogosTransportHost>> m_transports;
QString m_registryUrl;
QMap<QString, QString> m_tokens;
ModuleProxy* m_moduleProxy;
ModuleHandshakeProxy* m_handshakeProxy = nullptr;
QString m_registeredHandshakeName;
QtProviderObject* m_qtProviderObject;
QString m_registeredObjectName;
// Appended (never inserted mid-list): keeps every pre-existing member's
// offset identical between builds, so a LogosAPIProvider is layout-safe even
// if a future co-located consumer ever accessed one across a version
// boundary. Held until the proxy exists (registerObject may come after the
// setter).
TokenValidator m_pendingValidator;
};
#endif // LOGOS_API_PROVIDER_H