diff --git a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h index 0de341eb7..c5a3c99c3 100644 --- a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h +++ b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h @@ -604,11 +604,53 @@ ROSIDL_GENERATOR_C_PUBLIC rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * - referenced_descriptions, + referenced_descriptions, const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, rosidl_runtime_c__type_description__TypeDescription ** output_description, bool coerce_to_valid); +// Create a type description from a referenced description, then validate if coerce_to_valid is true +// This is done by copy!! This allocates memory and the caller is responsible for deallocating the +// output +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * + referenced_descriptions, + const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, + rosidl_runtime_c__type_description__TypeDescription ** output_description, + bool coerce_to_valid); + +/// In-place replace substrings in an individual description's type name and nested names in fields +/** + * This means the `IndividualTypeDescription`'s' type_name will get replaced and all references + * to any nested_type_names in the description's fields. + * + * NOTE(methylDragon): This method is pretty inefficient because it doesn't do checking and will + * re-copy all names. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, + const char * from, + const char * to); + + +/// In-place replace substrings across all type names (and references to those names) +/** + * This means all `IndividualTypeDescription` type_names will get replaced, in the main description + * and referenced type descriptions, and also all references to those names (nested_type_name.) + * + * NOTE(methylDragon): This method is pretty inefficient because it doesn't do checking and will + * re-copy all names. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_place( + rosidl_runtime_c__type_description__TypeDescription * type_description, + const char * from, + const char * to); // ================================================================================================= // DESCRIPTION PRINTING diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index eb07e384d..205f4025c 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -1233,7 +1234,7 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * - referenced_descriptions, + referenced_descriptions, const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, rosidl_runtime_c__type_description__TypeDescription ** output_description, bool coerce_to_valid) @@ -1272,7 +1273,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ if (coerce_to_valid) { rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( - *output_description); + *output_description); if (ret != RCUTILS_RET_OK) { RCUTILS_LOG_ERROR("Could not coerce output type description to valid"); rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); @@ -1284,6 +1285,109 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ } +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, + const char * from, + const char * to) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(individual_type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(from, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(to, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + bool ret = false; + char * repl = NULL; + + // Replace type name + repl = rcutils_repl_str( + individual_type_description->type_name.data, from, to, &allocator); + if (repl == NULL) { + RCUTILS_LOG_ERROR("Could not replace individual type description type name"); + return RCUTILS_RET_ERROR; + } + + ret = rosidl_runtime_c__String__assign(&(individual_type_description->type_name), repl); + allocator.deallocate(repl, allocator.state); + if (!ret) { + RCUTILS_LOG_ERROR("Could not assign individual type description type name"); + return RCUTILS_RET_ERROR; + } + + // Replace field nested type names + rosidl_runtime_c__type_description__Field * field = NULL; + if (individual_type_description->fields.data) { + for (size_t i = 0; i < individual_type_description->fields.size; i++) { + field = &individual_type_description->fields.data[i]; + if (!field->type.nested_type_name.size) { + continue; + } + + repl = rcutils_repl_str(field->type.nested_type_name.data, from, to, &allocator); + if (repl == NULL) { + RCUTILS_LOG_ERROR( + "Could not replace individual type description field nested type name. Beware! " + "Partial in-place replacements might have already happened!"); + return RCUTILS_RET_ERROR; + } + + ret = rosidl_runtime_c__String__assign(&(field->type.nested_type_name), repl); + allocator.deallocate(repl, allocator.state); + if (!ret) { + RCUTILS_LOG_ERROR( + "Could not assign individual type description field nested type name. Beware! " + "Partial in-place replacements might have already happened!"); + return RCUTILS_RET_ERROR; + } + } + } + return RCUTILS_RET_OK; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_place( + rosidl_runtime_c__type_description__TypeDescription * type_description, + const char * from, + const char * to) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(from, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(to, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_ret_t end_ret = RCUTILS_RET_ERROR; + + end_ret = + rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( + &type_description->type_description, from, to); + if (end_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not replace main type description type name"); + return end_ret; + } + + if (type_description->referenced_type_descriptions.data) { + for (size_t i = 0; i < type_description->referenced_type_descriptions.size; i++) { + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description = + &type_description->referenced_type_descriptions.data[i]; + + /* *INDENT-OFF* */ // Uncrustify will dislodge the NOLINT + end_ret = + rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( // NOLINT + individual_type_description, from, to); + /* *INDENT-ON* */ + + if (end_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR( + "Could not replace type names in referenced type. Beware! " + "Partial in-place replacements might have already happened!"); + return end_ret; + } + } + } + return RCUTILS_RET_OK; +} + + // ================================================================================================= // DESCRIPTION PRINTING // ================================================================================================= diff --git a/rosidl_runtime_c/test/test_type_description_utils.cpp b/rosidl_runtime_c/test/test_type_description_utils.cpp index 94255c876..ea23f66fb 100644 --- a/rosidl_runtime_c/test/test_type_description_utils.cpp +++ b/rosidl_runtime_c/test/test_type_description_utils.cpp @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include + #include "rosidl_runtime_c/type_description/field__functions.h" #include "rosidl_runtime_c/type_description/field__struct.h" #include "rosidl_runtime_c/type_description/individual_type_description__functions.h" @@ -19,9 +22,6 @@ #include "rosidl_runtime_c/type_description/type_description__functions.h" #include "rosidl_runtime_c/type_description/type_description__struct.h" #include "rosidl_runtime_c/type_description_utils.h" - -#include -#include #include #include @@ -127,7 +127,7 @@ TEST(TestUtils, test_basic_construction) } -class TestUtilsFixture: public ::testing::Test +class TestUtilsFixture : public ::testing::Test { public: void SetUp() @@ -374,8 +374,15 @@ TEST_F(TestUtilsFixture, test_appends_and_advanced_construction) EXPECT_TRUE( rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( &subset_desc_2->referenced_type_descriptions.data[0], empty_individual_desc)); - // rosidl_runtime_c_type_description_utils_print_type_description(type_description_1); - // rosidl_runtime_c_type_description_utils_print_type_description(subset_desc_2); + + // Test type name string replacements + ret = rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_place( + subset_desc_2, "mpty", "ligibility" // Expect eligibility + ); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_FALSE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &subset_desc_2->referenced_type_descriptions.data[0], empty_individual_desc)); rosidl_runtime_c__type_description__TypeDescription__destroy(subset_desc_2); }