diff --git a/backends/dealii/tests/modules.cc b/backends/dealii/tests/modules.cc index 18ac392..50fd6e0 100644 --- a/backends/dealii/tests/modules.cc +++ b/backends/dealii/tests/modules.cc @@ -113,6 +113,35 @@ TEST(Modules, SingleNodeNetwork) EXPECT_EQ(network.get_outputs().size(), 1u); } +TEST(Modules, NetworkNodeExposesDanglingSelfOutput) +{ + coral::register_all_types(); + + coral::Network inner_network; + inner_network.add_node(coral::make_node>(), + "triangulation"); + + nlohmann::json node_json = { + {"type", "coral::Network"}, + {"value", nlohmann::json(inner_network)} + }; + + coral::NodeObjectPtr network_node; + coral::from_json(node_json, network_node); + + const auto info = network_node->get_info(); + ASSERT_TRUE(info.contains("arguments")); + ASSERT_TRUE(info.contains("inputs")); + ASSERT_TRUE(info.contains("outputs")); + + ASSERT_EQ(info["arguments"].size(), 1u); + EXPECT_EQ(info["inputs"], nlohmann::json::array()); + EXPECT_EQ(info["outputs"], nlohmann::json::array({0})); + EXPECT_EQ(info["arguments"][0]["name"], "triangulation"); + EXPECT_EQ(info["arguments"][0]["connection_type"], "output"); + EXPECT_EQ(info["arguments"][0]["type"], "dealii::Triangulation<2, 2>"); +} + TEST(Modules, CompleteNetworkNoIO) { coral::register_all_types(); diff --git a/core/include/coral_network_implementation.h b/core/include/coral_network_implementation.h index c981313..41b2153 100644 --- a/core/include/coral_network_implementation.h +++ b/core/include/coral_network_implementation.h @@ -882,22 +882,42 @@ namespace coral throw std::runtime_error("Network interface node not found."); const auto info = node_it->second->get_info(); - if (!info.contains("arguments")) - throw std::runtime_error("Node is missing argument metadata."); + nlohmann::json arg_json; + if (entry.argument_index < 0) + { + if ((entry.type & ConnectionType::output) == ConnectionType::none) + throw std::runtime_error( + "Self ports can only contribute network outputs."); + + if (!info.contains("type")) + throw std::runtime_error("Node metadata missing type."); + + // SELF outputs are exposed using the node's externally connectable + // type. Derived nodes advertise their base type on SELF ports. + arg_json["type"] = info.contains("base") ? info["base"] : + info["type"]; + const auto node_name = get_node_name(entry.node_id); + arg_json["name"] = node_name.empty() ? "self" : node_name; + } + else + { + if (!info.contains("arguments")) + throw std::runtime_error("Node is missing argument metadata."); - if (entry.argument_index < 0 || - entry.argument_index >= static_cast(info["arguments"].size())) - throw std::runtime_error("Dangling argument index out of range."); + if (entry.argument_index >= + static_cast(info["arguments"].size())) + throw std::runtime_error("Dangling argument index out of range."); - const auto &arg_info = info["arguments"][entry.argument_index]; - if (!arg_info.contains("type")) - throw std::runtime_error("Argument metadata missing type."); + const auto &arg_info = info["arguments"][entry.argument_index]; + if (!arg_info.contains("type")) + throw std::runtime_error("Argument metadata missing type."); + + arg_json["type"] = arg_info["type"]; + if (arg_info.contains("name")) + arg_json["name"] = arg_info["name"]; + } - nlohmann::json arg_json; - arg_json["type"] = arg_info["type"]; arg_json["connection_type"] = magic_enum::enum_name(entry.type); - if (arg_info.contains("name")) - arg_json["name"] = arg_info["name"]; const auto arg_index = static_cast(interface.arguments.size()); @@ -973,8 +993,6 @@ namespace coral for (unsigned int i = 0; i < outputs.size(); ++i) { const int arg_index = outputs[i].get(); - if (arg_index < 0) - continue; bool connected = false; for (const auto &[conn_id, conn] : connections) {