Skip to content

Commit

Permalink
Return error on invalid YAML fields
Browse files Browse the repository at this point in the history
  • Loading branch information
marioprats committed Nov 22, 2023
1 parent 50277e3 commit cb3284f
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 0 deletions.
2 changes: 2 additions & 0 deletions dynmsg/include/dynmsg/msg_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ namespace c
* representation for the given ROS message.
*
* It is an error for the YAML representation to contain a field that is not in the ROS message.
* An std::runtime_error exception will be thrown in this case.
*
* It is not an error for a field of the ROS message to not be specified in the YAML
* representation; that field will be left uninitialised.
*/
Expand Down
15 changes: 15 additions & 0 deletions dynmsg/src/msg_parser_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <yaml-cpp/yaml.h>

#include <string>
#include <unordered_set>

#include "rosidl_runtime_c/string.h"
#include "rosidl_runtime_c/string_functions.h"
Expand Down Expand Up @@ -413,12 +414,23 @@ void yaml_to_rosmsg_impl(
const TypeInfo * typeinfo,
uint8_t * buffer)
{
// Put all the YAML node keys into a set. We'll be removing the keys from the set as they are parsed. If at the end
// there are some keys left in the set, it means the YAML has extra fields that don't belong to the message, and an
// exception will be thrown.
std::unordered_set<std::string> yaml_keys;
for (const auto& elem : root)
{
yaml_keys.insert(elem.first.as<std::string>());
}


for (uint32_t i = 0; i < typeinfo->member_count_; i++) {
const auto & member = typeinfo->members_[i];

if (!root[member.name_]) {
continue;
}
yaml_keys.erase(member.name_);

switch (member.type_id_) {
case rosidl_typesupport_introspection_c__ROS_TYPE_FLOAT:
Expand Down Expand Up @@ -480,6 +492,9 @@ void yaml_to_rosmsg_impl(
throw std::runtime_error("unknown type");
}
}
if (!yaml_keys.empty()) {
throw std::runtime_error("Found unknown fields in the YAML not corresponding to the given message.");
}
}

} // namespace impl
Expand Down
14 changes: 14 additions & 0 deletions dynmsg/src/msg_parser_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>

#include "rosidl_runtime_c/string.h"
#include "rosidl_runtime_c/string_functions.h"
Expand Down Expand Up @@ -502,6 +503,15 @@ void yaml_to_rosmsg_impl(
const TypeInfo_Cpp * typeinfo,
uint8_t * buffer)
{
// Put all the YAML node keys into a set. We'll be removing the keys from the set as they are
// parsed. If at the end there are some keys left in the set, it means the YAML has extra fields
// that don't belong to the message, and an exception will be thrown.
std::unordered_set<std::string> yaml_keys;
for (const auto& elem : root)
{
yaml_keys.insert(elem.first.as<std::string>());
}

DYNMSG_DEBUG(std::cout << "DEBUG: yaml_to_rosmsg_impl" << std::endl);
DYNMSG_DEBUG(
std::cout << "DEBUG: type_info message_namespace_: " <<
Expand All @@ -514,6 +524,7 @@ void yaml_to_rosmsg_impl(
if (!root[member.name_]) {
continue;
}
yaml_keys.erase(member.name_);

switch (member.type_id_) {
case rosidl_typesupport_introspection_cpp::ROS_TYPE_FLOAT:
Expand Down Expand Up @@ -575,6 +586,9 @@ void yaml_to_rosmsg_impl(
throw std::runtime_error("unknown type");
}
}
if (!yaml_keys.empty()) {
throw std::runtime_error("Found unknown fields in the YAML not corresponding to the given message.");
}
}

} // namespace impl
Expand Down
10 changes: 10 additions & 0 deletions dynmsg_demo/test/msg_parser_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ TEST(MsgParser, String) {
dynmsg::c::ros_message_destroy(&generic_msg);
}

TEST(MsgParser, StringInvalidFieldCpp) {
EXPECT_ANY_THROW(dynmsg::cpp::yaml_to_rosmsg(
InterfaceTypeName{"std_msgs", "String"}, "{ invalid_field: hello }"));
}

TEST(MsgParser, StringInvalidFieldC) {
EXPECT_ANY_THROW(dynmsg::c::yaml_to_rosmsg(
InterfaceTypeName{"std_msgs", "String"}, "{ invalid_field: hello }"));
}

TEST(MsgParser, WideString) {
auto generic_msg = dynmsg::c::yaml_to_rosmsg(
InterfaceTypeName{"dynmsg_msgs", "WideString"},
Expand Down

0 comments on commit cb3284f

Please sign in to comment.