diff --git a/pj_plugins/docs/message-parser-guide.md b/pj_plugins/docs/message-parser-guide.md index ed80a73..3d3bf88 100644 --- a/pj_plugins/docs/message-parser-guide.md +++ b/pj_plugins/docs/message-parser-guide.md @@ -23,7 +23,7 @@ the host, which routes them to the appropriate parser based on encoding name. 1. Subclass `PJ::MessageParserPluginBase` 2. Override `parse()` (required) and optionally `bindSchema()`, `saveConfig()`, `loadConfig()` -3. Export with `PJ_MESSAGE_PARSER_PLUGIN(YourClass, R"({"id":"...","name":"...","version":"...","encoding":"..."})")` +3. Export with `PJ_MESSAGE_PARSER_PLUGIN(YourClass, R"({"id":"...","name":"...","version":"...","encoding":["..."]})")` 4. Build as a shared library linking `pj_base` A complete example lives at `pj_plugins/examples/mock_json_parser.cpp`. @@ -79,7 +79,7 @@ manifest string literal (see Manifest Schema below): ```cpp PJ_MESSAGE_PARSER_PLUGIN(MyJsonParser, - R"({"id":"json-parser","name":"JSON Parser","version":"1.0.0","encoding":"json"})") + R"({"id":"json-parser","name":"JSON Parser","version":"1.0.0","encoding":["json"]})") ``` This generates the `extern "C"` entry point that the host resolves via dlsym. @@ -326,7 +326,7 @@ it without instantiating the plugin. | `id` | string | yes | Stable plugin identifier — used by the host catalog and the marketplace. Must be unique per plugin. | | `name` | string | yes | Human-readable plugin name. | | `version` | string | yes | Semver version string. | -| `encoding` | string | yes | Encoding this parser handles, e.g. `"json"`, `"protobuf"`, `"ros1msg"`. The host uses this to match binding requests to parsers. | +| `encoding` | array of strings | yes | Encodings this parser handles, e.g. `["json"]`, `["protobuf"]`, `["ros1msg", "ros2msg", "cdr"]`. The host uses this list to match binding requests to parsers; one parser may register more than one encoding. | Example: ```json @@ -334,7 +334,7 @@ Example: "id": "protobuf-parser", "name": "Protobuf Parser", "version": "1.0.0", - "encoding": "protobuf" + "encoding": ["protobuf"] } ``` diff --git a/pj_plugins/examples/mock_json_parser.cpp b/pj_plugins/examples/mock_json_parser.cpp index 9cd1a32..4fd2afb 100644 --- a/pj_plugins/examples/mock_json_parser.cpp +++ b/pj_plugins/examples/mock_json_parser.cpp @@ -23,4 +23,5 @@ class MockJsonParser : public PJ::MessageParserPluginBase { } // namespace PJ_MESSAGE_PARSER_PLUGIN( - MockJsonParser, R"({"id":"mock-json-parser","name":"Mock JSON Parser","version":"1.0.0","encoding":"json"})") + MockJsonParser, + R"({"id":"mock-json-parser","name":"Mock JSON Parser","version":"1.0.0","encoding":["json"]})") diff --git a/pj_plugins/examples/mock_schema_parser.cpp b/pj_plugins/examples/mock_schema_parser.cpp index 6d1eba1..1572f25 100644 --- a/pj_plugins/examples/mock_schema_parser.cpp +++ b/pj_plugins/examples/mock_schema_parser.cpp @@ -111,4 +111,4 @@ class MockSchemaParser : public PJ::MessageParserPluginBase { PJ_MESSAGE_PARSER_PLUGIN( MockSchemaParser, - R"({"id":"mock-schema-parser","name":"Mock Schema Parser","version":"1.0.0","encoding":"csv_pair"})") + R"({"id":"mock-schema-parser","name":"Mock Schema Parser","version":"1.0.0","encoding":["csv_pair"]})") diff --git a/pj_plugins/include/pj_plugins/host/plugin_catalog.hpp b/pj_plugins/include/pj_plugins/host/plugin_catalog.hpp index d13f35c..ae4a95a 100644 --- a/pj_plugins/include/pj_plugins/host/plugin_catalog.hpp +++ b/pj_plugins/include/pj_plugins/host/plugin_catalog.hpp @@ -42,7 +42,7 @@ struct PluginDescriptor { std::string version; std::string description; std::string category; - std::string encoding; ///< for message parsers + std::vector encoding; ///< for message parsers (one or more) std::vector file_extensions; ///< for data sources std::vector capabilities; ///< optional capability tags }; diff --git a/pj_plugins/src/plugin_catalog.cpp b/pj_plugins/src/plugin_catalog.cpp index 0236bbd..f822291 100644 --- a/pj_plugins/src/plugin_catalog.cpp +++ b/pj_plugins/src/plugin_catalog.cpp @@ -246,19 +246,14 @@ Expected decodeManifest( d.file_extensions = *file_extensions; d.capabilities = *capabilities; - if (family == PluginFamily::kMessageParser) { - auto encoding = requiredString("encoding"); - if (!encoding) { - return unexpected(encoding.error()); - } - d.encoding = *encoding; - } else { - auto encoding = optionalString("encoding"); - if (!encoding) { - return unexpected(encoding.error()); - } - d.encoding = *encoding; + auto encoding = readStringArray(j, "encoding"); + if (!encoding) { + return unexpected(encoding.error()); + } + if (family == PluginFamily::kMessageParser && encoding->empty()) { + return unexpected(std::string("plugin embedded manifest missing required encoding array")); } + d.encoding = std::move(*encoding); return d; } diff --git a/pj_plugins/src/plugin_runtime_catalog.cpp b/pj_plugins/src/plugin_runtime_catalog.cpp index 3283d56..03161c2 100644 --- a/pj_plugins/src/plugin_runtime_catalog.cpp +++ b/pj_plugins/src/plugin_runtime_catalog.cpp @@ -226,9 +226,7 @@ bool PluginRuntimeCatalog::loadAndRegisterMessageParser(const PluginDescriptor& loaded.id = descriptor.id; loaded.name = descriptor.name; loaded.version = descriptor.version; - if (!descriptor.encoding.empty()) { - loaded.encodings.push_back(descriptor.encoding); - } + loaded.encodings.insert(loaded.encodings.end(), descriptor.encoding.begin(), descriptor.encoding.end()); report(DiagnosticLevel::kInfo, loaded.id, "Loaded MessageParser " + loaded.name + " from " + loaded.path); message_parsers_.push_back(std::move(loaded)); diff --git a/pj_plugins/tests/message_parser_library_test.cpp b/pj_plugins/tests/message_parser_library_test.cpp index d0b85eb..67a160f 100644 --- a/pj_plugins/tests/message_parser_library_test.cpp +++ b/pj_plugins/tests/message_parser_library_test.cpp @@ -29,7 +29,7 @@ TEST(MessageParserLibraryTest, ManifestRoundTrip) { auto handle = library->createHandle(); EXPECT_TRUE(handle.valid()); EXPECT_NE(handle.manifest().find("Mock JSON Parser"), std::string::npos); - EXPECT_NE(handle.manifest().find("\"encoding\":\"json\""), std::string::npos); + EXPECT_NE(handle.manifest().find("\"encoding\":[\"json\"]"), std::string::npos); } TEST(MessageParserLibraryTest, BindAndParse) { diff --git a/pj_plugins/tests/plugin_catalog_test.cpp b/pj_plugins/tests/plugin_catalog_test.cpp index 4b47858..1f0511b 100644 --- a/pj_plugins/tests/plugin_catalog_test.cpp +++ b/pj_plugins/tests/plugin_catalog_test.cpp @@ -70,7 +70,7 @@ TEST_F(PluginCatalogTest, InspectMessageParserRequiresEncoding) { ASSERT_TRUE(descriptor.has_value()) << descriptor.error(); EXPECT_EQ(descriptor->id, "mock-json-parser"); EXPECT_EQ(descriptor->family, PluginFamily::kMessageParser); - EXPECT_EQ(descriptor->encoding, "json"); + EXPECT_EQ(descriptor->encoding, std::vector{"json"}); } TEST_F(PluginCatalogTest, InspectToolboxDsoUsesEmbeddedManifest) {