3 Commits
Author SHA1 Message Date
Dario LipicarandClaude Opus 5 461cfed52d refactor: the LIDL codec exists once (#117)
* refactor: the LIDL codec exists once

The cdylib generator emitted its own copy of the codec — ~186 lines of
C++-emitting-C++ mirroring logos-protocol's logos_codec.h by hand. Every codec
fix had to be written twice or it silently only half-applied, which happened
twice in a row recently (routing scalars through the codec + signedness; then
accepting 3.0 while still rejecting 3.7).

It was worse than duplication. The two copies had DRIFTED — the emitted integer
decode gated on is_number() where the canonical one checked is_number_integer()
|| is_number_unsigned() — and logos_json.h's byte helpers were the same mangled
symbols with weak linkage and DIFFERENT bodies as logos_codec.h's, both reaching
one program (module TUs compiled one; liblogos_protocol.a carries TUs that
included the other). Which body won was down to link order.

logos_json.h goes back to its documented charter — "LogosMap/LogosList aliases
for impl classes", per its own CMakeLists — and loses 77 lines. jsonToBytes moves
beside its sibling jsonToStringVec in logos_lp_client.h, rebuilt on the canonical
isTaggedBytes/b64UrlDecode; it keeps its own narrow spelling because every lp
decoder is documented to yield the default-constructed value on a mismatch,
which neither bytesFromJson (throws) nor bytesFromJsonLenient (accepts more) does.

Emptying it rather than making it include logos_codec.h is deliberate: some
thirty alias-only include sites across the module repos get ZERO new includes,
and logos-cpp-sdkConfig's "only dependency is nlohmann_json" stays true.

With the clash gone the generic half is deletable. emitGeneratedCodec becomes
emitRecordCodecs: one logos::detail::Codec<::Rec, void> per declared record, and
nothing else. That residue is irreducible — a LIDL `type` is a per-contract
struct whose fields exist only in that module's header, and C++17 has no field
reflection. Nesting composes for free: Codec<std::vector<Blob>> and deeper come
from the shared half once Codec<::Blob> exists.

One asymmetry dies with it. The scalar bstr decode and the [bstr] element decode
were different functions with different strictness, so echoBytes("hi") succeeded
while echoBytesList(["hi"]) threw — inside one module, for the same type. They
are one function now.

Build wiring: ONE line, in this repo's own test CMake, using a variable
nix/tests.nix already supplies. Nothing in logos-module-builder, logos-qt-sdk, or
any module repo.

verified: cpp-sdk + protocol suites green; test_fullapi_cpp, test_fullapi_ext_cpp
and test_basic_module_cpp build; test-modules 176/176. Conformance delta is
exactly one cell, baselined first in logos-test-modules#31.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: bump logos-protocol to the path-threaded bstr decoder

logos-protocol 4359557 (#33). Required by this branch, not incidental: deleting
the emitted codec swaps its path-carrying bstr decode for the canonical one, and
without #33 the canonical one reported "at value" instead of "[0].payload" —
losing the diagnostic exactly where a malformed bstr is hardest to find.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 16:01:14 -03:00
Dario LipicarandClaude Opus 4.8 c7444bc29a Follow-ups to #100: lp-consumer bstr decode, Qt-free cdylib event types, binary-event coverage (#102)
* cdylib events: Qt-free types, and drop the unused bytes encoder

Three follow-ups to the bstr event fix, all in the cdylib events sidecar --
a Qt-FREE translation unit:

- An `any`/map event parameter was emitted as a bare QVariant/QVariantMap,
  which does not compile there. Spell those as their nlohmann aliases
  (LogosMap / LogosList) and pull in <logos_json.h> when they appear.
- std::vector<std::vector<uint8_t>> fell through the impl-header parser's
  unknown-type fallback to `any`, so the cdylib gate admitted it and the
  generator then emitted QVariant. Parse it as `[bstr]` so the gate rejects it
  with a message naming the offending parameter.
- The bytes encoder was emitted into every module's sidecar, leaving an unused
  static function (-Wunused-function) wherever no event carries binary data.
  Emit it only when a bstr event parameter exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* lp consumer: decode bstr into std::vector<uint8_t>

The Qt-free (`lp`) consumer wrappers -- what every universal C++ module gets for
its dependencies -- had no QByteArray in their type tables, so a `bstr` event
parameter, method argument, or return degraded to QVariant and then to LogosMap.
A consumer subscribing to a binary event was handed the raw tagged JSON object
{"_bytes": "<base64url>"} instead of the bytes, with no generated decode.

Teach the tables about QByteArray (-> std::vector<uint8_t>) and marshal it
through the canonical tagged form in both directions: logos::bytesToJson on the
way out, logos::jsonToBytes on the way in. Those live in logos_json.h -- Qt-free
and protocol-free, so the generated wrappers and module code can share them.
The Qt apiStyle already did this via QByteArray::toBase64/fromBase64.

Without this, a subscriber written the obvious way --

    onBinaryReady([](const std::string&, const std::vector<uint8_t>& payload) {...})

-- compiles (nlohmann::json has an implicit conversion operator) and then throws
at runtime on every event, so the callback body silently never runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* tests: cover binary event payloads by value, not just by source text

The regression test for #99 asserts on generated source text, so it stays green
against an encoder that emits the wrong bytes. Add the value-level half:

- tests/sdk/test_logos_json_bytes.cpp exercises the canonical tagged-bytes codec
  against the RFC 4648 vectors, the URL-safe alphabet, every len%3 tail group,
  embedded NULs and high bytes, a 109,447-byte payload (the size from #99), and
  the lenient/padded decode paths.
- tests/experimental/test_lidl_gen_cdylib.cpp additionally pins the Qt-free
  spelling of JSON event payloads, the rejection of [bstr], and the omission of
  the bytes encoder from modules whose events carry no binary data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* doctests: prove a binary event payload survives the round trip

Neither doc-test covered bytes-in-an-event -- the gap #99 fell through. The
generator round-trip carried `bstr` only as a method argument and return, and
the composition doc-test, which is the one that actually runs two modules under
logoscore and subscribes to an event, carried only a string. So a generator that
dropped every bstr event argument kept both of them green.

- cpp-sdk-module-composition: greeter_module gains a `blobReady(label, payload)`
  event and an `emitBlob(size)` method; orchestrator_module subscribes and
  reports the length AND a checksum of what it received. Length alone would not
  catch a corrupted payload -- a wrong alphabet round-trips to the same size.
- cpp-sdk-generator-roundtrip: sensor_module gains a `capture(id, frame: bstr)`
  event, and a new step shows the generated event body encoding it through
  lidlBytesToJson rather than pushing it raw.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* logos_json.h: include <cstddef> for size_t

The tagged-bytes codec uses size_t but relied on it arriving transitively
through the other includes. Include <cstddef> directly so the header is
self-contained. (Copilot review, PR #102.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:50:40 -03:00
Iuri Matias 1468180b25 add support for new types (#50) 2026-04-13 13:29:26 -04:00