diff --git a/ros2_rust_humble.repos b/ros2_rust_humble.repos index ee7a6cdfb..a3db3ac51 100644 --- a/ros2_rust_humble.repos +++ b/ros2_rust_humble.repos @@ -23,3 +23,11 @@ repositories: type: git url: https://github.com/ros2/unique_identifier_msgs.git version: humble + ros2-rust/rosidl_rust: + type: git + url: https://github.com/ros2-rust/rosidl_rust.git + version: main + ros2-rust/rosidl_runtime_rs: + type: git + url: https://github.com/ros2-rust/rosidl_runtime_rs.git + version: main diff --git a/ros2_rust_iron.repos b/ros2_rust_iron.repos index caa51bdfe..a6d493422 100644 --- a/ros2_rust_iron.repos +++ b/ros2_rust_iron.repos @@ -27,3 +27,11 @@ repositories: type: git url: https://github.com/ros2/unique_identifier_msgs.git version: iron + ros2-rust/rosidl_rust: + type: git + url: https://github.com/ros2-rust/rosidl_rust.git + version: main + ros2-rust/rosidl_runtime_rs: + type: git + url: https://github.com/ros2-rust/rosidl_runtime_rs.git + version: main diff --git a/ros2_rust_jazzy.repos b/ros2_rust_jazzy.repos index 3f72f560d..55a336a11 100644 --- a/ros2_rust_jazzy.repos +++ b/ros2_rust_jazzy.repos @@ -27,3 +27,11 @@ repositories: type: git url: https://github.com/ros2/unique_identifier_msgs.git version: jazzy + ros2-rust/rosidl_rust: + type: git + url: https://github.com/ros2-rust/rosidl_rust.git + version: main + ros2-rust/rosidl_runtime_rs: + type: git + url: https://github.com/ros2-rust/rosidl_runtime_rs.git + version: main diff --git a/ros2_rust_rolling.repos b/ros2_rust_rolling.repos index 06e40bfab..7146c4c0c 100644 --- a/ros2_rust_rolling.repos +++ b/ros2_rust_rolling.repos @@ -27,3 +27,11 @@ repositories: type: git url: https://github.com/ros2/unique_identifier_msgs.git version: rolling + ros2-rust/rosidl_rust: + type: git + url: https://github.com/ros2-rust/rosidl_rust.git + version: main + ros2-rust/rosidl_runtime_rs: + type: git + url: https://github.com/ros2-rust/rosidl_runtime_rs.git + version: main diff --git a/rosidl_generator_rs/CMakeLists.txt b/rosidl_generator_rs/CMakeLists.txt deleted file mode 100644 index fd7d1c135..000000000 --- a/rosidl_generator_rs/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(rosidl_generator_rs) - -find_package(ament_cmake REQUIRED) -find_package(ament_cmake_python REQUIRED) -find_package(rosidl_generator_c REQUIRED) -find_package(rosidl_typesupport_interface REQUIRED) -find_package(rosidl_typesupport_introspection_c REQUIRED) - -if("$ENV{ROS_DISTRO}" STRLESS_EQUAL "humble") - find_package(rosidl_cmake REQUIRED) - ament_export_dependencies(rosidl_cmake) -endif() - -ament_export_dependencies(rmw) -ament_export_dependencies(rosidl_generator_c) - -ament_index_register_resource("rosidl_generator_packages") -ament_index_register_resource("rosidl_runtime_packages") - -ament_python_install_package(${PROJECT_NAME}) - -ament_package( - CONFIG_EXTRAS - "rosidl_generator_rs-extras.cmake.in" - "cmake/register_rs.cmake" -) - -install(DIRECTORY cmake - DESTINATION share/${PROJECT_NAME}) -ament_register_extension( - "rosidl_generate_idl_interfaces" - "rosidl_generator_rs" - "${PROJECT_SOURCE_DIR}/cmake/rosidl_generator_rs_generate_interfaces.cmake") - -install( - PROGRAMS bin/rosidl_generator_rs - DESTINATION lib/rosidl_generator_rs -) -install( - DIRECTORY cmake resource - DESTINATION share/${PROJECT_NAME} -) diff --git a/rosidl_generator_rs/bin/rosidl_generator_rs b/rosidl_generator_rs/bin/rosidl_generator_rs deleted file mode 100755 index 0bfcc2213..000000000 --- a/rosidl_generator_rs/bin/rosidl_generator_rs +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import sys - -try: - from rosidl_generator_rs import generate_rs -except ImportError: - # modifying sys.path and importing the Rust package with the same - # name as this script does not work on Windows - rosidl_generator_rs_root = os.path.dirname(os.path.dirname(__file__)) - rosidl_generator_rs_module = os.path.join( - rosidl_generator_rs_root, 'rosidl_generator_rs', '__init__.py') - if not os.path.exists(rosidl_generator_rs_module): - raise - from importlib.machinery import SourceFileLoader - - loader = SourceFileLoader('rosidl_generator_rs', rosidl_generator_rs_module) - rosidl_generator_rs = loader.load_module() - generate_rs = rosidl_generator_rs.generate_rs - - -def main(argv=sys.argv[1:]): - parser = argparse.ArgumentParser(description='Generate the Rust ROS interfaces.') - parser.add_argument( - '--generator-arguments-file', - required=True, - help='The location of the file containing the generator arguments') - parser.add_argument( - '--typesupport-impls', - required=True, - help='All the available typesupport implementations') - args = parser.parse_args(argv) - - return generate_rs( - args.generator_arguments_file, - args.typesupport_impls, - ) - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/rosidl_generator_rs/cmake/custom_command.cmake b/rosidl_generator_rs/cmake/custom_command.cmake deleted file mode 100644 index d16ccd863..000000000 --- a/rosidl_generator_rs/cmake/custom_command.cmake +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2017 Esteve Fernandez -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -add_custom_command( - OUTPUT - ${_generated_common_rs_files} - ${_generated_msg_rs_files} - ${_generated_srv_rs_files} - ${_generated_action_rs_files} - COMMAND ${PYTHON_EXECUTABLE} ${rosidl_generator_rs_BIN} - --generator-arguments-file "${generator_arguments_file}" - --typesupport-impls "${_typesupport_impls}" - DEPENDS ${target_dependencies} - COMMENT "Generating Rust code for ROS interfaces" - VERBATIM -) - -if(TARGET ${rosidl_generate_interfaces_TARGET}${_target_suffix}) - message(WARNING "Custom target ${rosidl_generate_interfaces_TARGET}${_target_suffix} already exists") -else() - add_custom_target( - ${rosidl_generate_interfaces_TARGET}${_target_suffix} ALL - DEPENDS - ${_generated_common_rs_files} - ${_generated_msg_rs_files} - ${_generated_srv_rs_files} - ${_generated_action_rs_files} - ) -endif() diff --git a/rosidl_generator_rs/cmake/register_rs.cmake b/rosidl_generator_rs/cmake/register_rs.cmake deleted file mode 100644 index 1dbfcdaed..000000000 --- a/rosidl_generator_rs/cmake/register_rs.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2016-2017 Esteve Fernandez -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -macro(rosidl_generator_rs_extras BIN GENERATOR_FILES TEMPLATE_DIR) - find_package(ament_cmake_core QUIET REQUIRED) - ament_register_extension( - "rosidl_generate_idl_interfaces" - "rosidl_generator_rs" - "rosidl_generator_rs_generate_interfaces.cmake") - - normalize_path(BIN "${BIN}") - set(rosidl_generator_rs_BIN "${BIN}") - - normalize_path(GENERATOR_FILES "${GENERATOR_FILES}") - set(rosidl_generator_rs_GENERATOR_FILES "${GENERATOR_FILES}") - - normalize_path(TEMPLATE_DIR "${TEMPLATE_DIR}") - set(rosidl_generator_rs_TEMPLATE_DIR "${TEMPLATE_DIR}") -endmacro() diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake deleted file mode 100644 index af4206161..000000000 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright 2016-2017 Esteve Fernandez -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if(NOT WIN32) - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") - elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error") - endif() -endif() - -set(_output_path - "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs/${PROJECT_NAME}") -set(_generated_common_rs_files "") - -set(_generated_msg_rs_files "") -set(_generated_srv_rs_files "") -set(_generated_action_rs_files "") - -set(_has_msg FALSE) -set(_has_srv FALSE) -set(_has_action FALSE) - -foreach(_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) - get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) - get_filename_component(_parent_folder "${_parent_folder}" NAME) - get_filename_component(_module_name "${_idl_file}" NAME_WE) - - if(_parent_folder STREQUAL "msg") - set(_has_msg TRUE) - set(_idl_files ${_idl_files} ${_idl_file}) - elseif(_parent_folder STREQUAL "srv") - set(_has_srv TRUE) - set(_idl_files ${_idl_files} ${_idl_file}) - elseif(_parent_folder STREQUAL "action") - set(_has_action TRUE) - set(_idl_files ${_idl_files} ${_idl_file}) - else() - message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") - endif() -endforeach() - -list(APPEND _generated_common_rs_files - "${_output_path}/rust/src/lib.rs" - "${_output_path}/rust/build.rs" - "${_output_path}/rust/Cargo.toml" -) - -if(${_has_msg}) - list(APPEND _generated_msg_rs_files - "${_output_path}/rust/src/msg.rs" - ) -endif() - -if(${_has_srv}) - list(APPEND _generated_srv_rs_files - "${_output_path}/rust/src/srv.rs" - ) -endif() - -if(${_has_action}) - list(APPEND _generated_action_rs_files - "${_output_path}/rust/src/action.rs" - ) -endif() - -set(_dependency_files "") -set(_dependencies "") -foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) - foreach(_idl_file ${${_pkg_name}_IDL_FILES}) - set(_abs_idl_file "${${_pkg_name}_DIR}/../${_idl_file}") - normalize_path(_abs_idl_file "${_abs_idl_file}") - list(APPEND _dependency_files "${_abs_idl_file}") - list(APPEND _dependencies "${_pkg_name}:${_abs_idl_file}") - endforeach() -endforeach() - -set(target_dependencies - "${rosidl_generator_rs_BIN}" - ${rosidl_generator_rs_GENERATOR_FILES} - "${rosidl_generator_rs_TEMPLATE_DIR}/action.rs.em" - "${rosidl_generator_rs_TEMPLATE_DIR}/msg_idiomatic.rs.em" - "${rosidl_generator_rs_TEMPLATE_DIR}/msg_rmw.rs.em" - "${rosidl_generator_rs_TEMPLATE_DIR}/msg.rs.em" - "${rosidl_generator_rs_TEMPLATE_DIR}/srv_idiomatic.rs.em" - "${rosidl_generator_rs_TEMPLATE_DIR}/srv_rmw.rs.em" - "${rosidl_generator_rs_TEMPLATE_DIR}/srv.rs.em" - ${rosidl_generate_interfaces_ABS_IDL_FILES} - ${_idl_files} - ${_dependency_files}) -foreach(dep ${target_dependencies}) - if(NOT EXISTS "${dep}") - message(FATAL_ERROR "Target dependency '${dep}' does not exist") - endif() -endforeach() - -set(generator_arguments_file "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs__arguments.json") -rosidl_write_generator_arguments( - "${generator_arguments_file}" - PACKAGE_NAME "${PROJECT_NAME}" - IDL_TUPLES "${rosidl_generate_interfaces_IDL_TUPLES}" - ROS_INTERFACE_FILES "${_idl_files}" - ROS_INTERFACE_DEPENDENCIES "${_dependencies}" - OUTPUT_DIR "${_output_path}" - TEMPLATE_DIR "${rosidl_generator_rs_TEMPLATE_DIR}" - TARGET_DEPENDENCIES ${target_dependencies} -) - -file(READ ${generator_arguments_file} contents) -string(REPLACE "\n}" - ",\n \"package_version\": \"${${PROJECT_NAME}_VERSION}\"\n}" contents ${contents}) -file(WRITE ${generator_arguments_file} ${contents}) - -file(MAKE_DIRECTORY "${_output_path}") - -set(_target_suffix "__rs") - -# needed to avoid multiple calls to the Rust generator trick copied from -# https://github.com/ros2/rosidl/blob/master/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake -set(_subdir "${CMAKE_CURRENT_BINARY_DIR}/${rosidl_generate_interfaces_TARGET}${_target_suffix}") -file(MAKE_DIRECTORY "${_subdir}") -file(READ "${rosidl_generator_rs_DIR}/custom_command.cmake" _custom_command) -file(WRITE "${_subdir}/CMakeLists.txt" "${_custom_command}") -add_subdirectory("${_subdir}" ${rosidl_generate_interfaces_TARGET}${_target_suffix}) - -add_dependencies(${rosidl_generate_interfaces_TARGET} ${rosidl_generate_interfaces_TARGET}${_target_suffix}) - -set_property( - SOURCE - ${_generated_common_rs_files} - ${_generated_msg_rs_files} - ${_generated_srv_rs_files} - ${_generated_action_rs_files} - PROPERTY GENERATED 1) - -set(_rsext_suffix "__rsext") -if(NOT rosidl_generate_interfaces_SKIP_INSTALL) - ament_index_register_resource("rust_packages") - install( - DIRECTORY "${_output_path}/rust" - DESTINATION "share/${PROJECT_NAME}" - ) -endif() - -if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) - if( - NOT _generated_msg_rs_files STREQUAL "" OR - NOT _generated_srv_rs_files STREQUAL "" OR - NOT _generated_action_rs_files STREQUAL "" - ) - # TODO(esteve): add linters for Rust files - endif() -endif() diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml deleted file mode 100644 index bd6124cf1..000000000 --- a/rosidl_generator_rs/package.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - rosidl_generator_rs - 0.4.1 - Generate the ROS interfaces in Rust. - Esteve Fernandez - Apache License 2.0 - Esteve Fernandez - - ament_cmake - ros_environment - - rosidl_runtime_rs - - ament_cmake - ros_environment - rosidl_cmake - rosidl_pycommon - rosidl_runtime_rs - rosidl_typesupport_c - rosidl_typesupport_interface - - - rosidl_generator_c - - rosidl_parser - - ament_cmake_gtest - ament_lint_auto - ament_lint_common - rosidl_cmake - rosidl_pycommon - rosidl_generator_c - - rosidl_generator_packages - rosidl_runtime_packages - - - ament_cmake - - diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em deleted file mode 100644 index 7c8f7fe30..000000000 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "@(package_name)" -version = "@(package_version)" -edition = "2021" - -[dependencies] -rosidl_runtime_rs = "0.4" -serde = { version = "1", optional = true, features = ["derive"] } -serde-big-array = { version = "0.5.1", optional = true } -@[for dep in dependency_packages]@ -@(dep) = "*" -@[end for]@ - -[features] -@{ -serde_features = ["dep:serde", "dep:serde-big-array", "rosidl_runtime_rs/serde"] -for dep in dependency_packages: - serde_features.append("{}/serde".format(dep)) -}@ -serde = @(serde_features) diff --git a/rosidl_generator_rs/resource/action.rs.em b/rosidl_generator_rs/resource/action.rs.em deleted file mode 100644 index 5f463f318..000000000 --- a/rosidl_generator_rs/resource/action.rs.em +++ /dev/null @@ -1,205 +0,0 @@ -@{ -from rosidl_parser.definition import ( - ACTION_FEEDBACK_MESSAGE_SUFFIX, - ACTION_FEEDBACK_SUFFIX, - ACTION_GOAL_SERVICE_SUFFIX, - ACTION_GOAL_SUFFIX, - ACTION_RESULT_SERVICE_SUFFIX, - ACTION_RESULT_SUFFIX, - SERVICE_REQUEST_MESSAGE_SUFFIX, - SERVICE_RESPONSE_MESSAGE_SUFFIX, -) - -action_msg_specs = [] - -for subfolder, action in action_specs: - action_msg_specs.append((subfolder, action.goal)) - action_msg_specs.append((subfolder, action.result)) - action_msg_specs.append((subfolder, action.feedback)) - action_msg_specs.append((subfolder, action.feedback_message)) - -action_srv_specs = [] - -for subfolder, action in action_specs: - action_srv_specs.append((subfolder, action.send_goal_service)) - action_srv_specs.append((subfolder, action.get_result_service)) -}@ - -pub mod rmw { -@{ -TEMPLATE( - 'msg_rmw.rs.em', - package_name=package_name, interface_path=interface_path, - msg_specs=action_msg_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) - -TEMPLATE( - 'srv_rmw.rs.em', - package_name=package_name, interface_path=interface_path, - srv_specs=action_srv_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ -} // mod rmw - -@{ -TEMPLATE( - 'msg_idiomatic.rs.em', - package_name=package_name, interface_path=interface_path, - msg_specs=action_msg_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ - -@{ -TEMPLATE( - 'srv_idiomatic.rs.em', - package_name=package_name, interface_path=interface_path, - srv_specs=action_srv_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ - -@[for subfolder, action_spec in action_specs] - -@{ -type_name = action_spec.namespaced_type.name -}@ - -#[link(name = "@(package_name)__rosidl_typesupport_c")] -extern "C" { - fn rosidl_typesupport_c__get_action_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; -} - -// Corresponds to @(package_name)__@(subfolder)__@(type_name) -pub struct @(type_name); - -impl rosidl_runtime_rs::Action for @(type_name) { - type Goal = crate::@(subfolder)::@(type_name)@(ACTION_GOAL_SUFFIX); - type Result = crate::@(subfolder)::@(type_name)@(ACTION_RESULT_SUFFIX); - type Feedback = crate::@(subfolder)::@(type_name)@(ACTION_FEEDBACK_SUFFIX); - - fn get_type_support() -> *const std::ffi::c_void { - // SAFETY: No preconditions for this function. - unsafe { rosidl_typesupport_c__get_action_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } - } -} - -impl rosidl_runtime_rs::ActionImpl for @(type_name) { - type GoalStatusMessage = action_msgs::msg::rmw::GoalStatusArray; - type FeedbackMessage = crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX); - - type SendGoalService = crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX); - type CancelGoalService = action_msgs::srv::rmw::CancelGoal; - type GetResultService = crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX); - - fn create_goal_request( - goal_id: &[u8; 16], - goal: crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SUFFIX), - ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { - crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { - goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id }, - goal, - } - } - - fn get_goal_request_uuid( - request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX), - ) -> &[u8; 16] { - &request.goal_id.uuid - } - - fn create_goal_response( - accepted: bool, - stamp: (i32, u32), - ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { - crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { - accepted, - stamp: builtin_interfaces::msg::rmw::Time { - sec: stamp.0, - nanosec: stamp.1, - }, - } - } - - fn get_goal_response_accepted( - response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), - ) -> bool { - response.accepted - } - - fn get_goal_response_stamp( - response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), - ) -> (i32, u32) { - (response.stamp.sec, response.stamp.nanosec) - } - - fn create_feedback_message( - goal_id: &[u8; 16], - feedback: crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX), - ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX) { - let mut message = crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)::default(); - message.goal_id.uuid = *goal_id; - message.feedback = feedback; - message - } - - fn get_feedback_message_uuid( - feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX), - ) -> &[u8; 16] { - &feedback.goal_id.uuid - } - - fn get_feedback_message_feedback( - feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX), - ) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX) { - &feedback.feedback - } - - fn create_result_request( - goal_id: &[u8; 16], - ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { - crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { - goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id }, - } - } - - fn get_result_request_uuid( - request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX), - ) -> &[u8; 16] { - &request.goal_id.uuid - } - - fn create_result_response( - status: i8, - result: crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX), - ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { - crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { - status, - result, - } - } - - fn get_result_response_result( - response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), - ) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX) { - &response.result - } - - fn get_result_response_status( - response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), - ) -> i8 { - response.status - } -} - -@[end for] diff --git a/rosidl_generator_rs/resource/build.rs.em b/rosidl_generator_rs/resource/build.rs.em deleted file mode 100644 index d861d0e81..000000000 --- a/rosidl_generator_rs/resource/build.rs.em +++ /dev/null @@ -1,10 +0,0 @@ -use std::path::Path; - -fn main() { - let lib_dir = Path::new("../../../lib") - .canonicalize() - .expect("Could not find '../../../lib'"); - // This allows building Rust packages that depend on message crates without - // sourcing the install directory first. - println!("cargo:rustc-link-search={}", lib_dir.display()); -} diff --git a/rosidl_generator_rs/resource/lib.rs.em b/rosidl_generator_rs/resource/lib.rs.em deleted file mode 100644 index 1ef77924c..000000000 --- a/rosidl_generator_rs/resource/lib.rs.em +++ /dev/null @@ -1,15 +0,0 @@ -#![allow(non_camel_case_types)] -#![allow(clippy::derive_partial_eq_without_eq)] -#![allow(clippy::upper_case_acronyms)] - -@[if len(msg_specs) > 0]@ -pub mod msg; -@[end if]@ - -@[if len(srv_specs) > 0]@ -pub mod srv; -@[end if]@ - -@[if len(action_specs) > 0]@ -pub mod action; -@[end if]@ diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em deleted file mode 100644 index 3f7d10e5f..000000000 --- a/rosidl_generator_rs/resource/msg.rs.em +++ /dev/null @@ -1,23 +0,0 @@ -pub mod rmw { -@{ -TEMPLATE( - 'msg_rmw.rs.em', - package_name=package_name, interface_path=interface_path, - msg_specs=msg_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ -} // mod rmw - -@{ -TEMPLATE( - 'msg_idiomatic.rs.em', - package_name=package_name, interface_path=interface_path, - msg_specs=msg_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ diff --git a/rosidl_generator_rs/resource/msg_idiomatic.rs.em b/rosidl_generator_rs/resource/msg_idiomatic.rs.em deleted file mode 100644 index e6ec28807..000000000 --- a/rosidl_generator_rs/resource/msg_idiomatic.rs.em +++ /dev/null @@ -1,238 +0,0 @@ -@{ -from rosidl_parser.definition import AbstractGenericString -from rosidl_parser.definition import Array -from rosidl_parser.definition import BasicType -from rosidl_parser.definition import BoundedSequence -from rosidl_parser.definition import BoundedString -from rosidl_parser.definition import NamedType -from rosidl_parser.definition import NamespacedType -from rosidl_parser.definition import UnboundedSequence -from rosidl_parser.definition import UnboundedString -from rosidl_parser.definition import UnboundedWString -}@ - -@# ################################################# -@# ############ Idiomatic message types ############ -@# ################################################# -@# These types use standard Rust containers where possible. -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -@[for subfolder, msg_spec in msg_specs]@ -@{ -type_name = msg_spec.structure.namespaced_type.name -}@ - -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub struct @(type_name) { -@[for member in msg_spec.structure.members]@ - @(pre_field_serde(member.type))pub @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type)), -@[end for]@ -} - -@[if msg_spec.constants]@ -impl @(type_name) { -@[for constant in msg_spec.constants]@ -@{ -comments = getattr(constant, 'get_comment_lines', lambda: [])() -}@ -@[ for line in comments]@ -@[ if line]@ - /// @(line) -@[ else]@ - /// -@[ end if]@ -@[ end for]@ -@[ if isinstance(constant.type, BasicType)]@ - pub const @(get_rs_name(constant.name)): @(get_rmw_rs_type(constant.type)) = @(constant_value_to_rs(constant.type, constant.value)); -@[ elif isinstance(constant.type, AbstractGenericString)]@ - pub const @(get_rs_name(constant.name)): &'static str = @(constant_value_to_rs(constant.type, constant.value)); -@[ else]@ -@{assert False, 'Unhandled constant type: ' + str(constant.type)}@ -@[ end if]@ -@[end for]@ -} -@[end if] - -impl Default for @(type_name) { - fn default() -> Self { -@# This has the benefit of automatically setting the right default values - ::from_rmw_message(crate::@(subfolder)::rmw::@(type_name)::default()) - } -} - -impl rosidl_runtime_rs::Message for @(type_name) { - type RmwMsg = crate::@(subfolder)::rmw::@(type_name); - - fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { - match msg_cow { - std::borrow::Cow::Owned(msg) => std::borrow::Cow::Owned(Self::RmwMsg { -@[for member in msg_spec.structure.members]@ -@# -@# -@# == Array == -@[ if isinstance(member.type, Array)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(|elem| elem.as_str().into()), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()), -@[ elif isinstance(member.type.value_type, BasicType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), -@[ end if]@ -@# -@# -@# == UnboundedString + UnboundedWString == -@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), -@# -@# -@# == UnboundedSequence == -@[ elif isinstance(member.type, UnboundedSequence)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .into_iter() - .map(|elem| elem.as_str().into()) - .collect(), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .into_iter() - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()) - .collect(), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).into(), -@[ end if]@ -@# -@# -@# == NamedType + NamespacedType == -@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ - @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Owned(msg.@(get_rs_name(member.name)))).into_owned(), -@# -@# -@# == Bounded and basic types == -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ end if]@ -@[end for]@ - }), - std::borrow::Cow::Borrowed(msg) => std::borrow::Cow::Owned(Self::RmwMsg { -@[for member in msg_spec.structure.members]@ -@# -@# -@# == Array == -@[ if isinstance(member.type, Array)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| elem.as_str().into()) - .collect::>() - .try_into() - .unwrap(), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) - .collect::>() - .try_into() - .unwrap(), -@[ elif isinstance(member.type.value_type, BasicType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), -@[ end if]@ -@# -@# -@# == UnboundedString + UnboundedWString == -@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), -@# -@# -@# == UnboundedSequence == -@[ elif isinstance(member.type, UnboundedSequence)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| elem.as_str().into()) - .collect(), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) - .collect(), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_slice().into(), -@[ end if]@ -@# -@# -@# == NamedType + NamespacedType == -@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ - @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Borrowed(&msg.@(get_rs_name(member.name)))).into_owned(), -@# -@# -@# == BasicType == -@[ elif isinstance(member.type, BasicType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@# -@# -@# == Bounded types == -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), -@[ end if]@ -@[end for]@ - }) - } - } - - fn from_rmw_message(msg: Self::RmwMsg) -> Self { - Self { -@[for member in msg_spec.structure.members]@ -@# -@# -@# == Array == -@[ if isinstance(member.type, Array)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(|elem| elem.to_string()), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ end if]@ -@# -@# -@# == UnboundedSequence == -@[ elif isinstance(member.type, UnboundedSequence)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .into_iter() -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - .map(|elem| elem.to_string()) -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message) -@[ end if]@ - .collect(), -@# -@# -@# == UnboundedString + UnboundedWString == -@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).to_string(), -@# -@# -@# == NamedType + NamespacedType == -@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ - @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::from_rmw_message(msg.@(get_rs_name(member.name))), -@# -@# -@# == Bounded and basic types == -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ end if]@ -@[end for]@ - } - } -} - -@[end for] diff --git a/rosidl_generator_rs/resource/msg_rmw.rs.em b/rosidl_generator_rs/resource/msg_rmw.rs.em deleted file mode 100644 index fbedd6d5f..000000000 --- a/rosidl_generator_rs/resource/msg_rmw.rs.em +++ /dev/null @@ -1,112 +0,0 @@ -@{ -from rosidl_parser.definition import AbstractGenericString -from rosidl_parser.definition import Array -from rosidl_parser.definition import BasicType -from rosidl_parser.definition import BoundedSequence -from rosidl_parser.definition import BoundedString -from rosidl_parser.definition import NamedType -from rosidl_parser.definition import NamespacedType -from rosidl_parser.definition import UnboundedSequence -from rosidl_parser.definition import UnboundedString -from rosidl_parser.definition import UnboundedWString -}@ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -@[for subfolder, msg_spec in msg_specs]@ -@{ -type_name = msg_spec.structure.namespaced_type.name -}@ - -#[link(name = "@(package_name)__rosidl_typesupport_c")] -extern "C" { - fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; -} - -#[link(name = "@(package_name)__rosidl_generator_c")] -extern "C" { - fn @(package_name)__@(subfolder)__@(type_name)__init(msg: *mut @(type_name)) -> bool; - fn @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>, size: usize) -> bool; - fn @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>); - fn @(package_name)__@(subfolder)__@(type_name)__Sequence__copy(in_seq: &rosidl_runtime_rs::Sequence<@(type_name)>, out_seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>) -> bool; -} - -@# Drop is not needed, since the default drop glue does the same as fini here: -@# it just calls the drop/fini functions of all fields -// Corresponds to @(package_name)__@(subfolder)__@(type_name) -#[repr(C)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub struct @(type_name) { -@[for member in msg_spec.structure.members]@ - @(pre_field_serde(member.type))pub @(get_rs_name(member.name)): @(get_rmw_rs_type(member.type)), -@[end for]@ -} - -@[if msg_spec.constants]@ -impl @(type_name) { -@[for constant in msg_spec.constants]@ -@{ -comments = getattr(constant, 'get_comment_lines', lambda: [])() -}@ -@[ for line in comments]@ -@[ if line]@ - /// @(line) -@[ else]@ - /// -@[ end if]@ -@[ end for]@ -@[ if isinstance(constant.type, BasicType)]@ - pub const @(get_rs_name(constant.name)): @(get_rmw_rs_type(constant.type)) = @(constant_value_to_rs(constant.type, constant.value)); -@[ elif isinstance(constant.type, AbstractGenericString)]@ - pub const @(get_rs_name(constant.name)): &'static str = @(constant_value_to_rs(constant.type, constant.value)); -@[ else]@ -@{assert False, 'Unhandled constant type: ' + str(constant.type)}@ -@[ end if]@ -@[end for]@ -} -@[end if] - -impl Default for @(type_name) { - fn default() -> Self { - unsafe { -@# // SAFETY: This is safe since a zeroed bit pattern always forms a valid message. - let mut msg = std::mem::zeroed(); -@# // SAFETY: This is safe since the precondititons for init() are fulfilled by giving it a zeroed message. - if !@(package_name)__@(subfolder)__@(type_name)__init(&mut msg as *mut _) { - panic!("Call to @(package_name)__@(subfolder)__@(type_name)__init() failed"); - } - msg - } - } -} - -impl rosidl_runtime_rs::SequenceAlloc for @(type_name) { - fn sequence_init(seq: &mut rosidl_runtime_rs::Sequence, size: usize) -> bool { - // SAFETY: This is safe since the pointer is guaranteed to be valid/initialized. - unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq as *mut _, size) } - } - fn sequence_fini(seq: &mut rosidl_runtime_rs::Sequence) { - // SAFETY: This is safe since the pointer is guaranteed to be valid/initialized. - unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq as *mut _) } - } - fn sequence_copy(in_seq: &rosidl_runtime_rs::Sequence, out_seq: &mut rosidl_runtime_rs::Sequence) -> bool { - // SAFETY: This is safe since the pointer is guaranteed to be valid/initialized. - unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__copy(in_seq, out_seq as *mut _) } - } -} - -impl rosidl_runtime_rs::Message for @(type_name) { - type RmwMsg = Self; - fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { msg_cow } - fn from_rmw_message(msg: Self::RmwMsg) -> Self { msg } -} - -impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { - const TYPE_NAME: &'static str = "@(package_name)/@(subfolder)/@(type_name)"; - fn get_type_support() -> *const std::ffi::c_void { - // SAFETY: No preconditions for this function. - unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } - } -} - -@[end for] diff --git a/rosidl_generator_rs/resource/srv.rs.em b/rosidl_generator_rs/resource/srv.rs.em deleted file mode 100644 index dd99e8e76..000000000 --- a/rosidl_generator_rs/resource/srv.rs.em +++ /dev/null @@ -1,23 +0,0 @@ -@{ -TEMPLATE( - 'srv_idiomatic.rs.em', - package_name=package_name, interface_path=interface_path, - srv_specs=srv_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -} - -pub mod rmw { -@{ -TEMPLATE( - 'srv_rmw.rs.em', - package_name=package_name, interface_path=interface_path, - srv_specs=srv_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ -} // mod rmw diff --git a/rosidl_generator_rs/resource/srv_idiomatic.rs.em b/rosidl_generator_rs/resource/srv_idiomatic.rs.em deleted file mode 100644 index 660f1a67c..000000000 --- a/rosidl_generator_rs/resource/srv_idiomatic.rs.em +++ /dev/null @@ -1,44 +0,0 @@ -@{ -req_res_specs = [] - -for subfolder, service in srv_specs: - req_res_specs.append((subfolder, service.request_message)) - req_res_specs.append((subfolder, service.response_message)) -}@ - -@{ -TEMPLATE( - 'msg_idiomatic.rs.em', - package_name=package_name, interface_path=interface_path, - msg_specs=req_res_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ - -@[for subfolder, srv_spec in srv_specs] - -@{ -type_name = srv_spec.namespaced_type.name -}@ - -#[link(name = "@(package_name)__rosidl_typesupport_c")] -extern "C" { - fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; -} - -// Corresponds to @(package_name)__@(subfolder)__@(type_name) -pub struct @(type_name); - -impl rosidl_runtime_rs::Service for @(type_name) { - type Request = crate::@(subfolder)::@(type_name)_Request; - type Response = crate::@(subfolder)::@(type_name)_Response; - - fn get_type_support() -> *const std::ffi::c_void { - // SAFETY: No preconditions for this function. - unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } - } -} - -@[end for] diff --git a/rosidl_generator_rs/resource/srv_rmw.rs.em b/rosidl_generator_rs/resource/srv_rmw.rs.em deleted file mode 100644 index 6ba55f1f5..000000000 --- a/rosidl_generator_rs/resource/srv_rmw.rs.em +++ /dev/null @@ -1,44 +0,0 @@ -@{ -req_res_specs = [] - -for subfolder, service in srv_specs: - req_res_specs.append((subfolder, service.request_message)) - req_res_specs.append((subfolder, service.response_message)) -}@ - -@{ -TEMPLATE( - 'msg_rmw.rs.em', - package_name=package_name, interface_path=interface_path, - msg_specs=req_res_specs, - get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - pre_field_serde=pre_field_serde, - get_idiomatic_rs_type=get_idiomatic_rs_type, - constant_value_to_rs=constant_value_to_rs) -}@ - -@[for subfolder, srv_spec in srv_specs] - -@{ -type_name = srv_spec.namespaced_type.name -}@ - - #[link(name = "@(package_name)__rosidl_typesupport_c")] - extern "C" { - fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; - } - - // Corresponds to @(package_name)__@(subfolder)__@(type_name) - pub struct @(type_name); - - impl rosidl_runtime_rs::Service for @(type_name) { - type Request = crate::@(subfolder)::rmw::@(type_name)_Request; - type Response = crate::@(subfolder)::rmw::@(type_name)_Response; - - fn get_type_support() -> *const std::ffi::c_void { - // SAFETY: No preconditions for this function. - unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } - } - } - -@[end for] diff --git a/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in b/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in deleted file mode 100644 index 14ba02d9f..000000000 --- a/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in +++ /dev/null @@ -1,8 +0,0 @@ -# generated from rosidl_generator_rs/rosidl_generator_rs-extras.cmake -find_package(rosidl_typesupport_c REQUIRED) -include("${CMAKE_CURRENT_LIST_DIR}/register_rs.cmake") -rosidl_generator_rs_extras( - "${rosidl_generator_rs_DIR}/../../../lib/rosidl_generator_rs/rosidl_generator_rs" - "${rosidl_generator_rs_DIR}/../../../@PYTHON_INSTALL_DIR@/rosidl_generator_rs/__init__.py" - "${rosidl_generator_rs_DIR}/../resource" -) diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py deleted file mode 100644 index b7850a6d8..000000000 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ /dev/null @@ -1,374 +0,0 @@ -# Copyright 2016-2017 Esteve Fernandez -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import pathlib - -from pathlib import Path - -if os.environ['ROS_DISTRO'] <= 'humble': - import rosidl_cmake as rosidl_pycommon -else: - import rosidl_pycommon - -from rosidl_parser.definition import AbstractGenericString -from rosidl_parser.definition import AbstractNestedType -from rosidl_parser.definition import AbstractSequence -from rosidl_parser.definition import AbstractString -from rosidl_parser.definition import AbstractWString -from rosidl_parser.definition import Action -from rosidl_parser.definition import Array -from rosidl_parser.definition import BasicType -from rosidl_parser.definition import BoundedSequence -from rosidl_parser.definition import BoundedString -from rosidl_parser.definition import BoundedWString -from rosidl_parser.definition import IdlContent -from rosidl_parser.definition import IdlLocator -from rosidl_parser.definition import Message -from rosidl_parser.definition import NamespacedType -from rosidl_parser.definition import Service -from rosidl_parser.definition import UnboundedSequence -from rosidl_parser.definition import UnboundedString -from rosidl_parser.definition import UnboundedWString - -from rosidl_parser.parser import parse_idl_file - - -# Taken from http://stackoverflow.com/a/6425628 -def convert_lower_case_underscore_to_camel_case(word): - return ''.join(x.capitalize() or '_' for x in word.split('_')) - - -def generate_rs(generator_arguments_file, typesupport_impls): - args = rosidl_pycommon.read_generator_arguments(generator_arguments_file) - package_name = args['package_name'] - - # expand init modules for each directory - modules = {} - idl_content = IdlContent() - dependency_packages = set() - - (Path(args['output_dir']) / 'rust/src').mkdir(parents=True, exist_ok=True) - - for dep_tuple in args.get('ros_interface_dependencies', []): - dep_parts = dep_tuple.split(':', 1) - assert len(dep_parts) == 2 - if dep_parts[0] != package_name: - dependency_packages.add(dep_parts[0]) - - for idl_tuple in args.get('idl_tuples', []): - idl_parts = idl_tuple.rsplit(':', 1) - assert len(idl_parts) == 2 - - idl_rel_path = pathlib.Path(idl_parts[1]) - idl_stems = modules.setdefault(str(idl_rel_path.parent), set()) - idl_stems.add(idl_rel_path.stem) - - locator = IdlLocator(*idl_parts) - idl_file = parse_idl_file(locator) - idl_content.elements += idl_file.content.elements - - typesupport_impls = typesupport_impls.split(';') - - template_dir = args['template_dir'] - - mapping_msgs = { - os.path.join(template_dir, 'msg.rs.em'): ['rust/src/%s.rs'], - } - - mapping_srvs = { - os.path.join(template_dir, 'srv.rs.em'): ['rust/src/%s.rs'], - } - - mapping_actions = { - os.path.join(template_dir, 'action.rs.em'): ['rust/src/%s.rs'], - } - - # Ensure the required templates exist - for template_file in mapping_msgs.keys(): - assert os.path.exists(template_file), \ - 'Messages template file %s not found' % template_file - for template_file in mapping_srvs.keys(): - assert os.path.exists(template_file), \ - 'Services template file %s not found' % template_file - for template_file in mapping_actions.keys(): - assert os.path.exists(template_file), \ - 'Actions template file %s not found' % template_file - - data = { - 'pre_field_serde': pre_field_serde, - 'get_rmw_rs_type': make_get_rmw_rs_type(args['package_name']), - 'get_rs_name': get_rs_name, - 'get_idiomatic_rs_type': make_get_idiomatic_rs_type(args['package_name']), - 'constant_value_to_rs': constant_value_to_rs, - 'value_to_rs': value_to_rs, - 'convert_camel_case_to_lower_case_underscore': - rosidl_pycommon.convert_camel_case_to_lower_case_underscore, - 'convert_lower_case_underscore_to_camel_case': - convert_lower_case_underscore_to_camel_case, - 'msg_specs': [], - 'srv_specs': [], - 'action_specs': [], - 'package_name': args['package_name'], - 'typesupport_impls': typesupport_impls, - 'interface_path': idl_rel_path, - } - - latest_target_timestamp = rosidl_pycommon.get_newest_modification_time( - args['target_dependencies']) - - for message in idl_content.get_elements_of_type(Message): - data['msg_specs'].append(('msg', message)) - - for service in idl_content.get_elements_of_type(Service): - data['srv_specs'].append(('srv', service)) - - for action in idl_content.get_elements_of_type(Action): - data['action_specs'].append(('action', action)) - - if data['msg_specs']: - for template_file, generated_filenames in mapping_msgs.items(): - for generated_filename in generated_filenames: - generated_file = os.path.join(args['output_dir'], - generated_filename % 'msg') - rosidl_pycommon.expand_template( - os.path.join(template_dir, template_file), - data.copy(), - generated_file, - minimum_timestamp=latest_target_timestamp) - - if data['srv_specs']: - for template_file, generated_filenames in mapping_srvs.items(): - for generated_filename in generated_filenames: - generated_file = os.path.join(args['output_dir'], - generated_filename % 'srv') - rosidl_pycommon.expand_template( - os.path.join(template_dir, template_file), - data.copy(), - generated_file, - minimum_timestamp=latest_target_timestamp) - - if data['action_specs']: - for template_file, generated_filenames in mapping_actions.items(): - for generated_filename in generated_filenames: - generated_file = os.path.join(args['output_dir'], - generated_filename % 'action') - rosidl_pycommon.expand_template( - os.path.join(template_dir, template_file), - data.copy(), - generated_file, - minimum_timestamp=latest_target_timestamp) - - rosidl_pycommon.expand_template( - os.path.join(template_dir, 'lib.rs.em'), - data.copy(), - os.path.join(args['output_dir'], 'rust/src/lib.rs'), - minimum_timestamp=latest_target_timestamp) - - cargo_toml_data = { - 'dependency_packages': dependency_packages, - 'package_name': args['package_name'], - 'package_version': args['package_version'], - } - rosidl_pycommon.expand_template( - os.path.join(template_dir, 'Cargo.toml.em'), - cargo_toml_data, - os.path.join(args['output_dir'], 'rust/Cargo.toml'), - minimum_timestamp=latest_target_timestamp) - - rosidl_pycommon.expand_template( - os.path.join(template_dir, 'build.rs.em'), - {}, - os.path.join(args['output_dir'], 'rust/build.rs'), - minimum_timestamp=latest_target_timestamp) - - return 0 - -def get_rs_name(name): - keywords = [ - # strict keywords - 'as', 'break', 'const', 'continue', 'crate', 'else', 'enum', 'extern', 'false', 'fn', 'for', 'if', 'for', - 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return', 'self', 'Self', 'static', - 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where', 'while', - # Edition 2018+ - 'async', 'await', 'dyn', - # Reserved - 'abstract', 'become', 'box', 'do', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual', - 'yield', 'try' - ] - # If the field name is a reserved keyword in Rust append an underscore - return name if not name in keywords else name + '_' - -def escape_string(s): - s = s.replace('\\', '\\\\') - s = s.replace("'", "\\'") - return s - - -def value_to_rs(type_, value): - assert type_.is_primitive_type() - assert value is not None - - if not type_.is_array: - return primitive_value_to_rs(type_, value) - - rs_values = [] - for single_value in value: - rs_value = primitive_value_to_rs(type_, single_value) - rs_values.append(rs_value) - return '{%s}' % ', '.join(rs_values) - - -def primitive_value_to_rs(type_, value): - assert type_.is_primitive_type() - assert value is not None - - if type_.type == 'bool': - return 'true' if value else 'false' - - if type_.type in [ - 'byte', - 'char', - 'wchar', - 'int8', - 'uint8', - 'int16', - 'uint16', - 'int32', - 'uint32', - 'int64', - 'uint64', - 'float64', - ]: - return str(value) - - if type_.type == 'float32': - return '%sf' % value - - if type_.type == 'string': - return '"%s"' % escape_string(value) - - assert False, "unknown primitive type '%s'" % type_ - - -def constant_value_to_rs(type_, value): - assert value is not None - - if isinstance(type_, BasicType): - if type_.typename == 'boolean': - return 'true' if value else 'false' - elif type_.typename == 'float32': - return '%sf' % value - return str(value) - - if isinstance(type_, AbstractGenericString): - return '"%s"' % escape_string(value) - - assert False, "unknown constant type '%s'" % type_ - -# Type hierarchy: -# -# AbstractType -# - AbstractNestableType -# - AbstractGenericString -# - AbstractString -# - BoundedString -# - UnboundedString -# - AbstractWString -# - BoundedWString -# - UnboundedWString -# - BasicType -# - NamedType -# - NamespacedType -# - AbstractNestedType -# - Array -# - AbstractSequence -# - BoundedSequence -# - UnboundedSequence - - -def pre_field_serde(type_): - if isinstance(type_, Array) and type_.size > 32: - return '#[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))]\n ' - else: - return '' - - -def make_get_idiomatic_rs_type(package_name): - get_rmw_rs_type = make_get_rmw_rs_type(package_name) - def get_idiomatic_rs_type(type_): - if isinstance(type_, UnboundedString) or isinstance(type_, UnboundedWString): - return 'std::string::String' - elif isinstance(type_, UnboundedSequence): - return 'Vec<{}>'.format(get_idiomatic_rs_type(type_.value_type)) - elif isinstance(type_, NamespacedType): - return '::'.join(type_.namespaced_name()).replace(package_name, 'crate') - elif isinstance(type_, Array): - return '[{}; {}]'.format(get_idiomatic_rs_type(type_.value_type), type_.size) - else: - return get_rmw_rs_type(type_) - return get_idiomatic_rs_type - -def make_get_rmw_rs_type(package_name): - def get_rmw_rs_type(type_): - if isinstance(type_, NamespacedType): - parts = list(type_.namespaced_name()) - parts.insert(-1, 'rmw') - return '::'.join(parts).replace(package_name, 'crate') - elif isinstance(type_, BasicType): - if type_.typename == 'boolean': - return 'bool' - elif type_.typename in ['byte', 'octet']: - return 'u8' - elif type_.typename == 'char': - return 'u8' - elif type_.typename == 'wchar': - return 'u16' - elif type_.typename == 'float': - return 'f32' - elif type_.typename == 'double': - return 'f64' - elif type_.typename == 'int8': - return 'i8' - elif type_.typename == 'uint8': - return 'u8' - elif type_.typename == 'int16': - return 'i16' - elif type_.typename == 'uint16': - return 'u16' - elif type_.typename == 'int32': - return 'i32' - elif type_.typename == 'uint32': - return 'u32' - elif type_.typename == 'int64': - return 'i64' - elif type_.typename == 'uint64': - return 'u64' - elif isinstance(type_, UnboundedString): - return 'rosidl_runtime_rs::String' - elif isinstance(type_, UnboundedWString): - return 'rosidl_runtime_rs::WString' - elif isinstance(type_, BoundedString): - return 'rosidl_runtime_rs::BoundedString<{}>'.format(type_.maximum_size) - elif isinstance(type_, BoundedWString): - return 'rosidl_runtime_rs::BoundedWString<{}>'.format(type_.maximum_size) - elif isinstance(type_, Array): - return '[{}; {}]'.format(get_rmw_rs_type(type_.value_type), type_.size) - elif isinstance(type_, UnboundedSequence): - return 'rosidl_runtime_rs::Sequence<{}>'.format(get_rmw_rs_type(type_.value_type)) - elif isinstance(type_, BoundedSequence): - return 'rosidl_runtime_rs::BoundedSequence<{}, {}>'.format(get_rmw_rs_type(type_.value_type), type_.maximum_size) - - assert False, "unknown type '%s'" % type_.typename - return get_rmw_rs_type diff --git a/rosidl_runtime_rs/Cargo.toml b/rosidl_runtime_rs/Cargo.toml deleted file mode 100644 index 4e4891638..000000000 --- a/rosidl_runtime_rs/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "rosidl_runtime_rs" -version = "0.4.1" -# This project is not military-sponsored, Jacob's employment contract just requires him to use this email address -authors = ["Esteve Fernandez ", "Nikolai Morin ", "Jacob Hassold "] -edition = "2021" -license = "Apache-2.0" -description = "Message generation code shared by Rust projects in ROS 2" - -[lib] -path = "src/lib.rs" - -# Please keep the list of dependencies alphabetically sorted, -# and also state why each dependency is needed. -[dependencies] -# Optional dependency for making it possible to convert messages to and from -# formats such as JSON, YAML, Pickle, etc. -serde = { version = "1", optional = true } - -[features] -default = [] -# This feature is solely for the purpose of being able to generate documetation without a ROS installation -# The only intended usage of this feature is for docs.rs builders to work, and is not intended to be used by end users -generate_docs = [] - -[dev-dependencies] -# Needed for writing property tests -quickcheck = "1" -# Needed for testing serde support -serde_json = "1" - -[build-dependencies] -# Needed for uploading documentation to docs.rs -cfg-if = "1.0.0" - -[package.metadata.docs.rs] -features = ["generate_docs"] diff --git a/rosidl_runtime_rs/README.md b/rosidl_runtime_rs/README.md deleted file mode 100644 index e391a4cab..000000000 --- a/rosidl_runtime_rs/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Common types and traits for ROS 2 messages in Rust - -ROS 2 is a popular open source robotics framework, used in a variety of fields (self-driving cars, drones, humanoid robots, etc.). `rosidl_runtime_rs` is a library that is mainly used by generated code for ROS 2 messages. - -Please see the docs in the [`ros2_rust` repo](https://github.com/ros2-rust/ros2_rust). \ No newline at end of file diff --git a/rosidl_runtime_rs/build.rs b/rosidl_runtime_rs/build.rs deleted file mode 100644 index fb98aeb30..000000000 --- a/rosidl_runtime_rs/build.rs +++ /dev/null @@ -1,33 +0,0 @@ -cfg_if::cfg_if! { - if #[cfg(not(feature="generate_docs"))] { - use std::env; - use std::path::Path; - - const AMENT_PREFIX_PATH: &str = "AMENT_PREFIX_PATH"; - - fn get_env_var_or_abort(env_var: &'static str) -> String { - if let Ok(value) = env::var(env_var) { - value - } else { - panic!( - "{} environment variable not set - please source ROS 2 installation first.", - env_var - ); - } - } - } -} - -fn main() { - #[cfg(not(feature = "generate_docs"))] - { - let ament_prefix_path_list = get_env_var_or_abort(AMENT_PREFIX_PATH); - for ament_prefix_path in ament_prefix_path_list.split(':') { - let library_path = Path::new(ament_prefix_path).join("lib"); - println!("cargo:rustc-link-search=native={}", library_path.display()); - } - } - - // Invalidate the built crate whenever this script changes - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/rosidl_runtime_rs/package.xml b/rosidl_runtime_rs/package.xml deleted file mode 100644 index 44e55f3aa..000000000 --- a/rosidl_runtime_rs/package.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - rosidl_runtime_rs - 0.4.1 - Message generation code shared by Rust projects in ROS 2 - - Jacob Hassold - Nikolai Morin - Apache License 2.0 - Jacob Hassold - Nikolai Morin - - rosidl_runtime_c - - ament_cargo - - diff --git a/rosidl_runtime_rs/src/lib.rs b/rosidl_runtime_rs/src/lib.rs deleted file mode 100644 index 7c5bad461..000000000 --- a/rosidl_runtime_rs/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![warn(missing_docs)] -//! Bindings to `rosidl_runtime_c` and related functionality for messages. - -#[macro_use] -mod sequence; -pub use sequence::{BoundedSequence, Sequence, SequenceExceedsBoundsError}; - -mod string; -pub use string::{BoundedString, BoundedWString, String, StringExceedsBoundsError, WString}; - -mod traits; -pub use traits::{Action, ActionImpl, Message, RmwMessage, SequenceAlloc, Service}; diff --git a/rosidl_runtime_rs/src/sequence.rs b/rosidl_runtime_rs/src/sequence.rs deleted file mode 100644 index 3d7cd5832..000000000 --- a/rosidl_runtime_rs/src/sequence.rs +++ /dev/null @@ -1,709 +0,0 @@ -use std::{ - cmp::Ordering, - fmt::{self, Debug, Display}, - hash::{Hash, Hasher}, - iter::{Extend, FromIterator, FusedIterator}, - ops::{Deref, DerefMut}, -}; - -#[cfg(feature = "serde")] -mod serde; - -use crate::traits::SequenceAlloc; - -/// An unbounded sequence. -/// -/// The layout of a concrete `Sequence` is the same as the corresponding `Sequence` struct -/// generated by `rosidl_generator_c`. For instance, -/// `rosidl_runtime_rs::Sequence` is the same -/// as `std_msgs__msg__String__Sequence`. See the [`Message`](crate::Message) trait for background -/// information on this topic. -/// -/// -/// # Example -/// -/// ``` -/// # use rosidl_runtime_rs::{Sequence, seq}; -/// let mut list = Sequence::::new(3); -/// // Sequences deref to slices -/// assert_eq!(&list[..], &[0, 0, 0]); -/// list[0] = 3; -/// list[1] = 2; -/// list[2] = 1; -/// assert_eq!(&list[..], &[3, 2, 1]); -/// // Alternatively, use the seq! macro -/// list = seq![3, 2, 1]; -/// // The default sequence is empty -/// assert!(Sequence::::default().is_empty()); -/// ``` -#[repr(C)] -pub struct Sequence { - data: *mut T, - size: usize, - capacity: usize, -} - -/// A bounded sequence. -/// -/// The layout of a concrete `BoundedSequence` is the same as the corresponding `Sequence` -/// struct generated by `rosidl_generator_c`. For instance, -/// `rosidl_runtime_rs::BoundedSequence` -/// is the same as `std_msgs__msg__String__Sequence`, which also represents both bounded -/// sequences. See the [`Message`](crate::Message) trait for background information on this -/// topic. -/// -/// # Example -/// -/// ``` -/// # use rosidl_runtime_rs::{BoundedSequence, seq}; -/// let mut list = BoundedSequence::::new(3); -/// // BoundedSequences deref to slices -/// assert_eq!(&list[..], &[0, 0, 0]); -/// list[0] = 3; -/// list[1] = 2; -/// list[2] = 1; -/// assert_eq!(&list[..], &[3, 2, 1]); -/// // Alternatively, use the seq! macro with the length specifier -/// list = seq![5 # 3, 2, 1]; -/// // The default bounded sequence is empty -/// assert!(BoundedSequence::::default().is_empty()); -/// ``` -#[derive(Clone)] -#[repr(transparent)] -pub struct BoundedSequence { - inner: Sequence, -} - -/// Error type for [`BoundedSequence::try_new()`]. -#[derive(Debug)] -pub struct SequenceExceedsBoundsError { - /// The actual length the sequence would have after the operation. - pub len: usize, - /// The upper bound on the sequence length. - pub upper_bound: usize, -} - -/// A by-value iterator created by [`Sequence::into_iter()`] and [`BoundedSequence::into_iter()`]. -pub struct SequenceIterator { - seq: Sequence, - idx: usize, -} - -// ========================= impl for Sequence ========================= - -impl Clone for Sequence { - fn clone(&self) -> Self { - let mut seq = Self::default(); - if T::sequence_copy(self, &mut seq) { - seq - } else { - panic!("Cloning Sequence failed") - } - } -} - -impl Debug for Sequence { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.as_slice().fmt(f) - } -} - -impl Default for Sequence { - fn default() -> Self { - Self { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - } - } -} - -impl Deref for Sequence { - type Target = [T]; - fn deref(&self) -> &Self::Target { - self.as_slice() - } -} - -impl DerefMut for Sequence { - fn deref_mut(&mut self) -> &mut Self::Target { - self.as_mut_slice() - } -} - -impl Drop for Sequence { - fn drop(&mut self) { - T::sequence_fini(self) - } -} - -impl Eq for Sequence {} - -impl Extend for Sequence { - fn extend(&mut self, iter: I) - where - I: IntoIterator, - { - let it = iter.into_iter(); - // The index in the sequence where the next element will be stored - let mut cur_idx = self.size; - // Convenience closure for resizing self - let resize = |seq: &mut Self, new_size: usize| { - let old_seq = std::mem::replace(seq, Sequence::new(new_size)); - for (i, elem) in old_seq.into_iter().enumerate().take(new_size) { - seq[i] = elem; - } - }; - // First, when there is a size hint > 0 (lower bound), make room for - // that many elements. - let num_remaining = it.size_hint().0; - if num_remaining > 0 { - let new_size = self.size.saturating_add(num_remaining); - resize(self, new_size); - } - for item in it { - // If there is no more capacity for the next element, resize to the - // next power of two. - // - // A pedantic implementation would check for usize overflow here, but - // that is hardly possible on real hardware. Also, not the entire - // usize address space is usable for user space programs. - if cur_idx == self.size { - let new_size = (self.size + 1).next_power_of_two(); - resize(self, new_size); - } - self[cur_idx] = item; - cur_idx += 1; - } - // All items from the iterator are stored. Shrink the sequence to fit. - if cur_idx < self.size { - resize(self, cur_idx); - } - } -} - -impl From<&[T]> for Sequence { - fn from(slice: &[T]) -> Self { - let mut seq = Sequence::new(slice.len()); - seq.clone_from_slice(slice); - seq - } -} - -impl From> for Sequence { - fn from(v: Vec) -> Self { - Sequence::from_iter(v) - } -} - -impl FromIterator for Sequence { - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - let mut seq = Sequence::new(0); - seq.extend(iter); - seq - } -} - -impl Hash for Sequence { - fn hash(&self, state: &mut H) { - self.as_slice().hash(state) - } -} - -impl IntoIterator for Sequence { - type Item = T; - type IntoIter = SequenceIterator; - fn into_iter(self) -> Self::IntoIter { - SequenceIterator { seq: self, idx: 0 } - } -} - -impl Ord for Sequence { - fn cmp(&self, other: &Self) -> Ordering { - self.as_slice().cmp(other.as_slice()) - } -} - -impl PartialEq for Sequence { - fn eq(&self, other: &Self) -> bool { - self.as_slice().eq(other.as_slice()) - } -} - -impl PartialOrd for Sequence { - fn partial_cmp(&self, other: &Self) -> Option { - self.as_slice().partial_cmp(other.as_slice()) - } -} - -// SAFETY: A sequence is a simple data structure, and therefore not thread-specific. -unsafe impl Send for Sequence {} -// SAFETY: A sequence does not have interior mutability, so it can be shared. -unsafe impl Sync for Sequence {} - -impl Sequence -where - T: SequenceAlloc, -{ - /// Creates a sequence of `len` elements with default values. - pub fn new(len: usize) -> Self { - let mut seq = Self::default(); - if !T::sequence_init(&mut seq, len) { - panic!("Sequence initialization failed"); - } - seq - } - - /// Extracts a slice containing the entire sequence. - /// - /// Equivalent to `&seq[..]`. - pub fn as_slice(&self) -> &[T] { - if self.data.is_null() { - &[] - } else { - // SAFETY: self.data is not null and points to self.size consecutive, - // initialized elements and isn't modified externally. - unsafe { std::slice::from_raw_parts(self.data, self.size) } - } - } - - /// Extracts a mutable slice containing the entire sequence. - /// - /// Equivalent to `&mut seq[..]`. - pub fn as_mut_slice(&mut self) -> &mut [T] { - if self.data.is_null() { - &mut [] - } else { - // SAFETY: self.data is not null and points to self.size consecutive, - // initialized elements and isn't modified externally. - unsafe { std::slice::from_raw_parts_mut(self.data, self.size) } - } - } -} - -// ========================= impl for BoundedSequence ========================= - -impl Debug for BoundedSequence { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.as_slice().fmt(f) - } -} - -impl Default for BoundedSequence { - fn default() -> Self { - Self { - inner: Sequence { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - }, - } - } -} - -impl Deref for BoundedSequence { - type Target = [T]; - fn deref(&self) -> &Self::Target { - self.inner.deref() - } -} - -impl DerefMut for BoundedSequence { - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.deref_mut() - } -} - -impl Drop for BoundedSequence { - fn drop(&mut self) { - T::sequence_fini(&mut self.inner) - } -} - -impl Eq for BoundedSequence {} - -impl Extend for BoundedSequence { - fn extend(&mut self, iter: I) - where - I: IntoIterator, - { - self.inner - .extend(iter.into_iter().take(N - self.inner.size)); - } -} - -impl TryFrom<&[T]> for BoundedSequence { - type Error = SequenceExceedsBoundsError; - fn try_from(slice: &[T]) -> Result { - let mut seq = BoundedSequence::try_new(slice.len())?; - seq.clone_from_slice(slice); - Ok(seq) - } -} - -impl TryFrom> for BoundedSequence { - type Error = SequenceExceedsBoundsError; - fn try_from(v: Vec) -> Result { - if v.len() > N { - Err(SequenceExceedsBoundsError { - len: v.len(), - upper_bound: N, - }) - } else { - Ok(BoundedSequence::from_iter(v)) - } - } -} - -impl FromIterator for BoundedSequence { - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - let mut seq = BoundedSequence::new(0); - seq.extend(iter); - seq - } -} - -impl Hash for BoundedSequence { - fn hash(&self, state: &mut H) { - self.as_slice().hash(state) - } -} - -impl IntoIterator for BoundedSequence { - type Item = T; - type IntoIter = SequenceIterator; - fn into_iter(mut self) -> Self::IntoIter { - let seq = std::mem::replace( - &mut self.inner, - Sequence { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - }, - ); - SequenceIterator { seq, idx: 0 } - } -} - -impl Ord for BoundedSequence { - fn cmp(&self, other: &Self) -> Ordering { - self.as_slice().cmp(other.as_slice()) - } -} - -impl PartialEq for BoundedSequence { - fn eq(&self, other: &Self) -> bool { - self.as_slice().eq(other.as_slice()) - } -} - -impl PartialOrd for BoundedSequence { - fn partial_cmp(&self, other: &Self) -> Option { - self.as_slice().partial_cmp(other.as_slice()) - } -} - -impl BoundedSequence -where - T: SequenceAlloc, -{ - /// Creates a sequence of `len` elements with default values. - /// - /// If `len` is greater than `N`, this function panics. - pub fn new(len: usize) -> Self { - Self::try_new(len).unwrap() - } - - /// Attempts to create a sequence of `len` elements with default values. - /// - /// If `len` is greater than `N`, this function returns an error. - pub fn try_new(len: usize) -> Result { - if len > N { - return Err(SequenceExceedsBoundsError { - len, - upper_bound: N, - }); - } - let mut seq = Self::default(); - if !T::sequence_init(&mut seq.inner, len) { - panic!("BoundedSequence initialization failed"); - } - Ok(seq) - } - - /// Extracts a slice containing the entire sequence. - /// - /// Equivalent to `&seq[..]`. - pub fn as_slice(&self) -> &[T] { - self.inner.as_slice() - } - - /// Extracts a mutable slice containing the entire sequence. - /// - /// Equivalent to `&mut seq[..]`. - pub fn as_mut_slice(&mut self) -> &mut [T] { - self.inner.as_mut_slice() - } -} - -// ========================= impl for SequenceIterator ========================= - -impl Iterator for SequenceIterator { - type Item = T; - fn next(&mut self) -> Option { - if self.idx >= self.seq.size { - return None; - } - // SAFETY: data + idx is in bounds and points to a valid value - let elem = unsafe { - let ptr = self.seq.data.add(self.idx); - let elem = ptr.read(); - // Need to make sure that dropping the sequence later will not fini() the elements - ptr.write(std::mem::zeroed::()); - elem - }; - self.idx += 1; - Some(elem) - } - - fn size_hint(&self) -> (usize, Option) { - let len = (self.seq.size + 1) - self.idx; - (len, Some(len)) - } -} - -impl ExactSizeIterator for SequenceIterator { - fn len(&self) -> usize { - (self.seq.size + 1) - self.idx - } -} - -impl FusedIterator for SequenceIterator {} - -// ========================= impl for StringExceedsBoundsError ========================= - -impl Display for SequenceExceedsBoundsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "BoundedSequence with upper bound {} initialized with len {}", - self.upper_bound, self.len - ) - } -} - -impl std::error::Error for SequenceExceedsBoundsError {} - -macro_rules! impl_sequence_alloc_for_primitive_type { - ($rust_type:ty, $init_func:ident, $fini_func:ident, $copy_func:ident) => { - #[link(name = "rosidl_runtime_c")] - extern "C" { - fn $init_func(seq: *mut Sequence<$rust_type>, size: usize) -> bool; - fn $fini_func(seq: *mut Sequence<$rust_type>); - fn $copy_func( - in_seq: *const Sequence<$rust_type>, - out_seq: *mut Sequence<$rust_type>, - ) -> bool; - } - - impl SequenceAlloc for $rust_type { - fn sequence_init(seq: &mut Sequence, size: usize) -> bool { - // SAFETY: There are no special preconditions to the sequence_init function. - unsafe { - // This allocates space and sets seq.size and seq.capacity to size - let ret = $init_func(seq as *mut _, size); - if !seq.data.is_null() { - // Zero memory, since it will be uninitialized if there is no default value - std::ptr::write_bytes(seq.data, 0u8, size); - } - ret - } - } - fn sequence_fini(seq: &mut Sequence) { - // SAFETY: There are no special preconditions to the sequence_fini function. - unsafe { $fini_func(seq as *mut _) } - } - fn sequence_copy(in_seq: &Sequence, out_seq: &mut Sequence) -> bool { - // SAFETY: There are no special preconditions to the sequence_copy function. - unsafe { $copy_func(in_seq as *const _, out_seq as *mut _) } - } - } - }; -} - -// Primitives are not messages themselves, but there can be sequences of them. -// -// See https://github.com/ros2/rosidl/blob/master/rosidl_runtime_c/include/rosidl_runtime_c/primitives_sequence.h -// Long double isn't available in Rust, so it is skipped. -impl_sequence_alloc_for_primitive_type!( - f32, - rosidl_runtime_c__float__Sequence__init, - rosidl_runtime_c__float__Sequence__fini, - rosidl_runtime_c__float__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - f64, - rosidl_runtime_c__double__Sequence__init, - rosidl_runtime_c__double__Sequence__fini, - rosidl_runtime_c__double__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - bool, - rosidl_runtime_c__boolean__Sequence__init, - rosidl_runtime_c__boolean__Sequence__fini, - rosidl_runtime_c__boolean__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - u8, - rosidl_runtime_c__uint8__Sequence__init, - rosidl_runtime_c__uint8__Sequence__fini, - rosidl_runtime_c__uint8__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - i8, - rosidl_runtime_c__int8__Sequence__init, - rosidl_runtime_c__int8__Sequence__fini, - rosidl_runtime_c__int8__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - u16, - rosidl_runtime_c__uint16__Sequence__init, - rosidl_runtime_c__uint16__Sequence__fini, - rosidl_runtime_c__uint16__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - i16, - rosidl_runtime_c__int16__Sequence__init, - rosidl_runtime_c__int16__Sequence__fini, - rosidl_runtime_c__int16__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - u32, - rosidl_runtime_c__uint32__Sequence__init, - rosidl_runtime_c__uint32__Sequence__fini, - rosidl_runtime_c__uint32__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - i32, - rosidl_runtime_c__int32__Sequence__init, - rosidl_runtime_c__int32__Sequence__fini, - rosidl_runtime_c__int32__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - u64, - rosidl_runtime_c__uint64__Sequence__init, - rosidl_runtime_c__uint64__Sequence__fini, - rosidl_runtime_c__uint64__Sequence__copy -); -impl_sequence_alloc_for_primitive_type!( - i64, - rosidl_runtime_c__int64__Sequence__init, - rosidl_runtime_c__int64__Sequence__fini, - rosidl_runtime_c__int64__Sequence__copy -); - -/// Creates a sequence, similar to the `vec!` macro. -/// -/// It's possible to create both [`Sequence`]s and [`BoundedSequence`]s. -/// Unbounded sequences are created by a comma-separated list of values. -/// Bounded sequences are created by additionally specifying the maximum capacity (the `N` type -/// parameter) in the beginning, followed by a `#`. -/// -/// # Example -/// ``` -/// # use rosidl_runtime_rs::{BoundedSequence, Sequence, seq}; -/// let unbounded: Sequence = seq![1, 2, 3]; -/// let bounded: BoundedSequence = seq![5 # 1, 2, 3]; -/// assert_eq!(&unbounded[..], &bounded[..]) -/// ``` -#[macro_export] -macro_rules! seq { - [$( $elem:expr ),*] => { - { - let len = seq!(@count_tts $($elem),*); - let mut seq = Sequence::new(len); - let mut i = 0; - $( - seq[i] = $elem; - #[allow(unused_assignments)] - { i += 1; } - )* - seq - } - }; - [$len:literal # $( $elem:expr ),*] => { - { - let len = seq!(@count_tts $($elem),*); - let mut seq = BoundedSequence::<_, $len>::new(len); - let mut i = 0; - $( - seq[i] = $elem; - #[allow(unused_assignments)] - { i += 1; } - )* - seq - } - }; - // https://danielkeep.github.io/tlborm/book/blk-counting.html - (@replace_expr ($_t:expr, $sub:expr)) => {$sub}; - (@count_tts $($e:expr),*) => {<[()]>::len(&[$(seq!(@replace_expr ($e, ()))),*])}; -} - -#[cfg(test)] -mod tests { - use quickcheck::{quickcheck, Arbitrary, Gen}; - - use super::*; - - impl Arbitrary for Sequence { - fn arbitrary(g: &mut Gen) -> Self { - Vec::arbitrary(g).into() - } - } - - impl Arbitrary for BoundedSequence { - fn arbitrary(g: &mut Gen) -> Self { - let len = u8::arbitrary(g); - (0..len).map(|_| T::arbitrary(g)).collect() - } - } - - #[test] - fn test_empty_sequence() { - assert!(Sequence::::default().is_empty()); - assert!(BoundedSequence::::default().is_empty()); - } - - quickcheck! { - fn test_extend(xs: Vec, ys: Vec) -> bool { - let mut xs_seq = Sequence::new(xs.len()); - xs_seq.copy_from_slice(&xs); - xs_seq.extend(ys.clone()); - if xs_seq.len() != xs.len() + ys.len() { - return false; - } - if xs_seq[..xs.len()] != xs[..] { - return false; - } - if xs_seq[xs.len()..] != ys[..] { - return false; - } - true - } - } - - quickcheck! { - fn test_iteration(xs: Vec) -> bool { - let mut seq_1 = Sequence::new(xs.len()); - seq_1.copy_from_slice(&xs); - let seq_2 = seq_1.clone().into_iter().collect(); - seq_1 == seq_2 - } - } -} diff --git a/rosidl_runtime_rs/src/sequence/serde.rs b/rosidl_runtime_rs/src/sequence/serde.rs deleted file mode 100644 index f3af605c6..000000000 --- a/rosidl_runtime_rs/src/sequence/serde.rs +++ /dev/null @@ -1,71 +0,0 @@ -use serde::{de::Error, ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; - -use super::{BoundedSequence, Sequence}; -use crate::traits::SequenceAlloc; - -impl<'de, T: Deserialize<'de> + SequenceAlloc> Deserialize<'de> for Sequence { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let v: Vec<_> = Deserialize::deserialize(deserializer)?; - Ok(Self::from(v)) - } -} - -impl Serialize for Sequence { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(Some(self.len()))?; - for element in self.iter() { - seq.serialize_element(element)?; - } - seq.end() - } -} - -impl<'de, T: Deserialize<'de> + SequenceAlloc, const N: usize> Deserialize<'de> - for BoundedSequence -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let v: Vec<_> = Deserialize::deserialize(deserializer)?; - Self::try_from(v).map_err(D::Error::custom) - } -} - -impl Serialize for BoundedSequence { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.inner.serialize(serializer) - } -} - -#[cfg(test)] -mod tests { - use quickcheck::quickcheck; - - use crate::{BoundedSequence, Sequence}; - - quickcheck! { - fn test_json_roundtrip_sequence(xs: Sequence) -> bool { - let value = serde_json::to_value(xs.clone()).unwrap(); - let recovered = serde_json::from_value(value).unwrap(); - xs == recovered - } - } - - quickcheck! { - fn test_json_roundtrip_bounded_sequence(xs: BoundedSequence) -> bool { - let value = serde_json::to_value(xs.clone()).unwrap(); - let recovered = serde_json::from_value(value).unwrap(); - xs == recovered - } - } -} diff --git a/rosidl_runtime_rs/src/string.rs b/rosidl_runtime_rs/src/string.rs deleted file mode 100644 index 9f0251cda..000000000 --- a/rosidl_runtime_rs/src/string.rs +++ /dev/null @@ -1,600 +0,0 @@ -use std::{ - borrow::Borrow, - cmp::Ordering, - ffi::CStr, - fmt::{self, Debug, Display}, - hash::{Hash, Hasher}, - ops::{Deref, DerefMut}, -}; - -#[cfg(feature = "serde")] -mod serde; - -use crate::{sequence::Sequence, traits::SequenceAlloc}; - -/// A zero-terminated UTF-8 string. -/// -/// The layout of this type is the same as `rosidl_runtime_c__String`. See the -/// [`Message`](crate::Message) trait for background information on this topic. -/// -/// -/// # Example -/// -/// ``` -/// # use rosidl_runtime_rs::String; -/// let mut s = String::from("Grüß Gott!"); -/// // Conversion back to a std::string::String is done with the ToString trait from the standard -/// // library. -/// assert_eq!(&s.to_string(), "Grüß Gott!"); -/// ``` -#[repr(C)] -pub struct String { - /// Dynamic memory in this type is allocated and deallocated by C, but this is a detail that is managed by - /// the relevant functions and trait impls. - data: *mut std::os::raw::c_char, - size: usize, - capacity: usize, -} - -/// A zero-terminated UTF-16 string. -/// -/// The layout of this type is the same as `rosidl_runtime_c__U16String`. See the -/// [`Message`](crate::Message) trait for background information on this topic. -/// -/// # Example -/// -/// ``` -/// # use rosidl_runtime_rs::WString; -/// let mut s = WString::from("Grüß Gott!"); -/// // Conversion back to a std::string::String is done with the ToString trait from the standard -/// // library. -/// assert_eq!(&s.to_string(), "Grüß Gott!"); -/// ``` -#[repr(C)] -pub struct WString { - data: *mut u16, - size: usize, - capacity: usize, -} - -/// A zero-terminated UTF-8 string with a length limit. -/// -/// The same as [`String`], but it cannot be constructed from a string that is too large. -/// The length is measured as the number of bytes. -/// -/// # Example -/// -/// ``` -/// # use rosidl_runtime_rs::BoundedString; -/// let mut maybe_str = BoundedString::<3>::try_from("noo!"); -/// assert!(maybe_str.is_err()); -/// maybe_str = BoundedString::<3>::try_from("ok!"); -/// assert!(maybe_str.is_ok()); -/// let bounded_str = maybe_str.unwrap(); -/// assert_eq!(&bounded_str.to_string(), "ok!"); -/// ``` -#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct BoundedString { - inner: String, -} - -/// A zero-terminated UTF-16 string with a length limit. -/// -/// The same as [`WString`], but it cannot be constructed from a string that is too large. -/// The length is measured as the number of 16-bit words. -/// -/// # Example -/// -/// ``` -/// # use rosidl_runtime_rs::BoundedWString; -/// let mut maybe_wstr = BoundedWString::<3>::try_from("noo!"); -/// assert!(maybe_wstr.is_err()); -/// maybe_wstr = BoundedWString::<3>::try_from("ok!"); -/// assert!(maybe_wstr.is_ok()); -/// let bounded_wstr = maybe_wstr.unwrap(); -/// assert_eq!(&bounded_wstr.to_string(), "ok!"); -/// ``` -#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct BoundedWString { - inner: WString, -} - -/// Error type for [`BoundedString::try_from()`] and [`BoundedWString::try_from()`]. -#[derive(Debug)] -pub struct StringExceedsBoundsError { - /// The actual length the string would have after the operation. - pub len: usize, - /// The upper bound on the string length. - pub upper_bound: usize, -} - -// ========================= impls for String and WString ========================= - -// There is a lot of redundancy between String and WString, which this macro aims to reduce. -macro_rules! string_impl { - ($string:ty, $char_type:ty, $unsigned_char_type:ty, $string_conversion_func:ident, $init:ident, $fini:ident, $assignn:ident, $sequence_init:ident, $sequence_fini:ident, $sequence_copy:ident) => { - #[link(name = "rosidl_runtime_c")] - extern "C" { - fn $init(s: *mut $string) -> bool; - fn $fini(s: *mut $string); - fn $assignn(s: *mut $string, value: *const $char_type, n: usize) -> bool; - fn $sequence_init(seq: *mut Sequence<$string>, size: usize) -> bool; - fn $sequence_fini(seq: *mut Sequence<$string>); - fn $sequence_copy( - in_seq: *const Sequence<$string>, - out_seq: *mut Sequence<$string>, - ) -> bool; - } - - impl Clone for $string { - fn clone(&self) -> Self { - let mut msg = Self::default(); - // SAFETY: This is doing the same thing as rosidl_runtime_c__String__copy. - if !unsafe { $assignn(&mut msg as *mut _, self.data as *const _, self.size) } { - panic!("$assignn failed"); - } - msg - } - } - - impl Debug for $string { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - Debug::fmt(&self.to_string(), f) - } - } - - impl Default for $string { - fn default() -> Self { - let mut msg = Self { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - }; - // SAFETY: Passing in a zeroed string is safe. - if !unsafe { $init(&mut msg as *mut _) } { - panic!("$init failed"); - } - msg - } - } - - // It's not guaranteed that there are no interior null bytes, hence no Deref to CStr. - // This does not include the null byte at the end! - impl Deref for $string { - type Target = [$char_type]; - fn deref(&self) -> &Self::Target { - // SAFETY: self.data points to self.size consecutive, initialized elements and - // isn't modified externally. - unsafe { std::slice::from_raw_parts(self.data, self.size) } - } - } - - impl DerefMut for $string { - fn deref_mut(&mut self) -> &mut Self::Target { - // SAFETY: self.data points to self.size consecutive, initialized elements and - // isn't modified externally. - unsafe { std::slice::from_raw_parts_mut(self.data, self.size) } - } - } - - impl Display for $string { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - // SAFETY: Same as deref, but with an additional cast to the unsigned type. - // See also https://users.rust-lang.org/t/how-to-convert-i8-to-u8/16308/11 - let u8_slice = unsafe { - std::slice::from_raw_parts(self.data as *mut $unsigned_char_type, self.size) - }; - let converted = std::string::String::$string_conversion_func(u8_slice); - Display::fmt(&converted, f) - } - } - - impl Drop for $string { - fn drop(&mut self) { - // SAFETY: There are no special preconditions to the fini function. - unsafe { - $fini(self as *mut _); - } - } - } - - impl Eq for $string {} - - impl Extend for $string { - fn extend>(&mut self, iter: I) { - let mut s = self.to_string(); - s.extend(iter); - *self = Self::from(s.as_str()); - } - } - - impl<'a> Extend<&'a char> for $string { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } - } - - impl FromIterator for $string { - fn from_iter>(iter: I) -> Self { - let mut buf = <$string>::default(); - buf.extend(iter); - buf - } - } - - impl<'a> FromIterator<&'a char> for $string { - fn from_iter>(iter: I) -> Self { - let mut buf = <$string>::default(); - buf.extend(iter); - buf - } - } - - impl Hash for $string { - fn hash(&self, state: &mut H) { - self.deref().hash(state) - } - } - - impl Ord for $string { - fn cmp(&self, other: &Self) -> Ordering { - self.deref().cmp(other.deref()) - } - } - - impl PartialEq for $string { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(other.deref()) - } - } - - impl PartialOrd for $string { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - // SAFETY: A string is a simple data structure, and therefore not thread-specific. - unsafe impl Send for $string {} - // SAFETY: A string does not have interior mutability, so it can be shared. - unsafe impl Sync for $string {} - - impl SequenceAlloc for $string { - fn sequence_init(seq: &mut Sequence, size: usize) -> bool { - // SAFETY: There are no special preconditions to the sequence_init function. - unsafe { $sequence_init(seq as *mut _, size) } - } - fn sequence_fini(seq: &mut Sequence) { - // SAFETY: There are no special preconditions to the sequence_fini function. - unsafe { $sequence_fini(seq as *mut _) } - } - fn sequence_copy(in_seq: &Sequence, out_seq: &mut Sequence) -> bool { - // SAFETY: There are no special preconditions to the sequence_copy function. - unsafe { $sequence_copy(in_seq as *const _, out_seq as *mut _) } - } - } - }; -} - -string_impl!( - String, - std::os::raw::c_char, - u8, - from_utf8_lossy, - rosidl_runtime_c__String__init, - rosidl_runtime_c__String__fini, - rosidl_runtime_c__String__assignn, - rosidl_runtime_c__String__Sequence__init, - rosidl_runtime_c__String__Sequence__fini, - rosidl_runtime_c__String__Sequence__copy -); -string_impl!( - WString, - u16, - u16, - from_utf16_lossy, - rosidl_runtime_c__U16String__init, - rosidl_runtime_c__U16String__fini, - rosidl_runtime_c__U16String__assignn, - rosidl_runtime_c__U16String__Sequence__init, - rosidl_runtime_c__U16String__Sequence__fini, - rosidl_runtime_c__U16String__Sequence__copy -); - -impl From for String -where - T: Borrow, -{ - fn from(s: T) -> Self { - let mut msg = Self { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - }; - let s = s.borrow(); - // SAFETY: It's okay to pass a non-zero-terminated string here since assignn uses the - // specified length and will append the 0 byte to the dest string itself. - if !unsafe { - rosidl_runtime_c__String__assignn(&mut msg as *mut _, s.as_ptr() as *const _, s.len()) - } { - panic!("rosidl_runtime_c__String__assignn failed"); - } - msg - } -} - -impl String { - /// Creates a CStr from this String. - /// - /// This scales with the length of the string but does not create copy of the string. - /// See also [`CStr::from_ptr()`]. - pub fn to_cstr(&self) -> &CStr { - // SAFETY: self.data is a valid pointer and won't change. - // Also, the lifetime of the CStr is the same as self, which is correct. - unsafe { CStr::from_ptr(self.data as *const _) } - } -} - -impl From<&str> for WString { - fn from(s: &str) -> Self { - let mut msg = Self { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - }; - let buf: Vec = s.encode_utf16().collect(); - // SAFETY: It's okay to pass a non-zero-terminated string here since assignn uses the - // specified length and will append the 0 to the dest string itself. - if !unsafe { - rosidl_runtime_c__U16String__assignn( - &mut msg as *mut _, - buf.as_ptr() as *const _, - buf.len(), - ) - } { - panic!("rosidl_runtime_c__U16String__assignn failed"); - } - msg - } -} - -// ========================= impl for BoundedString ========================= - -impl Debug for BoundedString { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - Debug::fmt(&self.inner, f) - } -} - -impl Deref for BoundedString { - type Target = [std::os::raw::c_char]; - fn deref(&self) -> &Self::Target { - self.inner.deref() - } -} - -impl DerefMut for BoundedString { - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.deref_mut() - } -} - -impl Display for BoundedString { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - Display::fmt(&self.inner, f) - } -} - -impl SequenceAlloc for BoundedString { - fn sequence_init(seq: &mut Sequence, size: usize) -> bool { - // SAFETY: There are no special preconditions to the rosidl_runtime_c__String__Sequence__init function. - unsafe { - rosidl_runtime_c__String__Sequence__init(seq as *mut Sequence as *mut _, size) - } - } - fn sequence_fini(seq: &mut Sequence) { - // SAFETY: There are no special preconditions to the rosidl_runtime_c__String__Sequence__fini function. - unsafe { rosidl_runtime_c__String__Sequence__fini(seq as *mut Sequence as *mut _) } - } - fn sequence_copy(in_seq: &Sequence, out_seq: &mut Sequence) -> bool { - // SAFETY: Transmute of a transparent type to the inner type is fine - unsafe { - ::sequence_copy( - std::mem::transmute::<&Sequence, &Sequence>(in_seq), - std::mem::transmute::<&mut Sequence, &mut Sequence>(out_seq), - ) - } - } -} - -impl TryFrom<&str> for BoundedString { - type Error = StringExceedsBoundsError; - fn try_from(s: &str) -> Result { - let length = s.len(); - if length <= N { - Ok(Self { - inner: String::from(s), - }) - } else { - Err(StringExceedsBoundsError { - len: length, - upper_bound: N, - }) - } - } -} - -// ========================= impl for BoundedWString ========================= - -impl Debug for BoundedWString { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - Debug::fmt(&self.inner, f) - } -} - -impl Deref for BoundedWString { - type Target = [u16]; - fn deref(&self) -> &Self::Target { - self.inner.deref() - } -} - -impl DerefMut for BoundedWString { - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.deref_mut() - } -} - -impl Display for BoundedWString { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - Display::fmt(&self.inner, f) - } -} - -impl SequenceAlloc for BoundedWString { - fn sequence_init(seq: &mut Sequence, size: usize) -> bool { - // SAFETY: There are no special preconditions to the rosidl_runtime_c__U16String__Sequence__init function. - unsafe { - rosidl_runtime_c__U16String__Sequence__init(seq as *mut Sequence as *mut _, size) - } - } - fn sequence_fini(seq: &mut Sequence) { - // SAFETY: There are no special preconditions to the rosidl_runtime_c__U16String__Sequence__fini function. - unsafe { rosidl_runtime_c__U16String__Sequence__fini(seq as *mut Sequence as *mut _) } - } - fn sequence_copy(in_seq: &Sequence, out_seq: &mut Sequence) -> bool { - // SAFETY: Transmute of a transparent type to the inner type is fine - unsafe { - ::sequence_copy( - std::mem::transmute::<&Sequence, &Sequence>(in_seq), - std::mem::transmute::<&mut Sequence, &mut Sequence>(out_seq), - ) - } - } -} - -impl TryFrom<&str> for BoundedWString { - type Error = StringExceedsBoundsError; - fn try_from(s: &str) -> Result { - let inner = WString::from(s); - if inner.size <= N { - Ok(Self { inner }) - } else { - Err(StringExceedsBoundsError { - len: inner.size, - upper_bound: N, - }) - } - } -} - -// ========================= impl for StringExceedsBoundsError ========================= - -impl Display for StringExceedsBoundsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "BoundedString with upper bound {} initialized with len {}", - self.upper_bound, self.len - ) - } -} - -impl std::error::Error for StringExceedsBoundsError {} - -#[cfg(test)] -mod tests { - use quickcheck::{Arbitrary, Gen}; - - use super::*; - - impl Arbitrary for String { - fn arbitrary(g: &mut Gen) -> Self { - std::string::String::arbitrary(g).as_str().into() - } - } - - impl Arbitrary for WString { - fn arbitrary(g: &mut Gen) -> Self { - std::string::String::arbitrary(g).as_str().into() - } - } - - impl Arbitrary for BoundedString<256> { - fn arbitrary(g: &mut Gen) -> Self { - let len = u8::arbitrary(g); - let s: std::string::String = (0..len).map(|_| char::arbitrary(g)).collect(); - s.as_str().try_into().unwrap() - } - } - - impl Arbitrary for BoundedWString<256> { - fn arbitrary(g: &mut Gen) -> Self { - let len = u8::arbitrary(g); - let s: std::string::String = (0..len).map(|_| char::arbitrary(g)).collect(); - s.as_str().try_into().unwrap() - } - } - - #[test] - fn string_from_char_iterator() { - // Base char case - let expected = String::from("abc"); - let actual = "abc".chars().collect::(); - - assert_eq!(expected, actual); - - // Empty case - let expected = String::from(""); - let actual = "".chars().collect::(); - - assert_eq!(expected, actual); - - // Non-ascii char case - let expected = String::from("Grüß Gott! 𝕊"); - let actual = "Grüß Gott! 𝕊".chars().collect::(); - - assert_eq!(expected, actual); - } - - #[test] - fn extend_string_with_char_iterator() { - let expected = WString::from("abcdef"); - let mut actual = WString::from("abc"); - actual.extend("def".chars()); - - assert_eq!(expected, actual); - } - - #[test] - fn wstring_from_char_iterator() { - // Base char case - let expected = WString::from("abc"); - let actual = "abc".chars().collect::(); - - assert_eq!(expected, actual); - - // Empty case - let expected = WString::from(""); - let actual = "".chars().collect::(); - - assert_eq!(expected, actual); - - // Non-ascii char case - let expected = WString::from("Grüß Gott! 𝕊"); - let actual = "Grüß Gott! 𝕊".chars().collect::(); - - assert_eq!(expected, actual); - } - - #[test] - fn extend_wstring_with_char_iterator() { - let expected = WString::from("abcdef"); - let mut actual = WString::from("abc"); - actual.extend("def".chars()); - - assert_eq!(expected, actual); - } -} diff --git a/rosidl_runtime_rs/src/string/serde.rs b/rosidl_runtime_rs/src/string/serde.rs deleted file mode 100644 index 0ded3fd4a..000000000 --- a/rosidl_runtime_rs/src/string/serde.rs +++ /dev/null @@ -1,209 +0,0 @@ -use std::fmt; - -use serde::{ - de::{Error, SeqAccess, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; - -use super::{ - rosidl_runtime_c__String__assignn, rosidl_runtime_c__U16String__assignn, BoundedString, - BoundedWString, String, WString, -}; - -struct StringVisitor; -struct WStringVisitor; - -impl Visitor<'_> for StringVisitor { - type Value = String; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string") - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - Ok(String::from(v)) - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: Error, - { - let mut msg = String { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - }; - // SAFETY: This is doing the same thing as rosidl_runtime_c__String__copy. - unsafe { - rosidl_runtime_c__String__assignn(&mut msg, v.as_ptr() as *const _, v.len()); - } - Ok(msg) - } - - // We don't implement visit_bytes_buf, since the data in a string must always be managed by C. -} - -impl<'de> Visitor<'de> for WStringVisitor { - type Value = WString; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string") - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - Ok(WString::from(v)) - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut buf = if let Some(size) = seq.size_hint() { - Vec::with_capacity(size) - } else { - Vec::new() - }; - while let Some(el) = seq.next_element::()? { - buf.push(el); - } - let mut msg = WString { - data: std::ptr::null_mut(), - size: 0, - capacity: 0, - }; - // SAFETY: This is doing the same thing as rosidl_runtime_c__U16String__copy. - unsafe { - rosidl_runtime_c__U16String__assignn(&mut msg, buf.as_ptr(), buf.len()); - } - Ok(msg) - } - - // We don't implement visit_bytes_buf, since the data in a string must always be managed by C. -} - -impl<'de> Deserialize<'de> for String { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_string(StringVisitor) - } -} - -impl Serialize for String { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // Not particularly efficient - // SAFETY: See the Display implementation. - let u8_slice = unsafe { std::slice::from_raw_parts(self.data as *mut u8, self.size) }; - let s = std::string::String::from_utf8_lossy(u8_slice); - serializer.serialize_str(&s) - } -} - -impl<'de> Deserialize<'de> for WString { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_string(WStringVisitor) - } -} - -impl Serialize for WString { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // Not particularly efficient - // SAFETY: See the Display implementation. - let u16_slice = unsafe { std::slice::from_raw_parts(self.data, self.size) }; - let s = std::string::String::from_utf16_lossy(u16_slice); - serializer.serialize_str(&s) - } -} - -impl<'de, const N: usize> Deserialize<'de> for BoundedString { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - std::string::String::deserialize(deserializer) - .and_then(|s| Self::try_from(s.as_str()).map_err(D::Error::custom)) - } -} - -impl Serialize for BoundedString { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.inner.serialize(serializer) - } -} - -impl<'de, const N: usize> Deserialize<'de> for BoundedWString { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - std::string::String::deserialize(deserializer) - .and_then(|s| Self::try_from(s.as_str()).map_err(D::Error::custom)) - } -} - -impl Serialize for BoundedWString { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.inner.serialize(serializer) - } -} - -#[cfg(test)] -mod tests { - use quickcheck::quickcheck; - - use crate::{BoundedString, BoundedWString, String, WString}; - - quickcheck! { - fn test_json_roundtrip_string(s: String) -> bool { - let value = serde_json::to_value(s.clone()).unwrap(); - let recovered = serde_json::from_value(value).unwrap(); - s == recovered - } - } - - quickcheck! { - fn test_json_roundtrip_wstring(s: WString) -> bool { - let value = serde_json::to_value(s.clone()).unwrap(); - let recovered = serde_json::from_value(value).unwrap(); - s == recovered - } - } - - quickcheck! { - fn test_json_roundtrip_bounded_string(s: BoundedString<256>) -> bool { - let value = serde_json::to_value(s.clone()).unwrap(); - let recovered = serde_json::from_value(value).unwrap(); - s == recovered - } - } - - quickcheck! { - fn test_json_roundtrip_bounded_wstring(s: BoundedWString<256>) -> bool { - let value = serde_json::to_value(s.clone()).unwrap(); - let recovered = serde_json::from_value(value).unwrap(); - s == recovered - } - } -} diff --git a/rosidl_runtime_rs/src/traits.rs b/rosidl_runtime_rs/src/traits.rs deleted file mode 100644 index 61d8c2392..000000000 --- a/rosidl_runtime_rs/src/traits.rs +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2020 DCS Corporation, All Rights Reserved. - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// DISTRIBUTION A. Approved for public release; distribution unlimited. -// OPSEC #4584. -// -use std::{borrow::Cow, fmt::Debug}; - -/// Internal trait that connects a particular `Sequence` instance to generated C functions -/// that allocate and deallocate memory. -/// -/// User code never needs to call these trait methods, much less implement this trait. -pub trait SequenceAlloc: Sized { - /// Wraps the corresponding init function generated by `rosidl_generator_c`. - fn sequence_init(seq: &mut crate::Sequence, size: usize) -> bool; - /// Wraps the corresponding fini function generated by `rosidl_generator_c`. - fn sequence_fini(seq: &mut crate::Sequence); - /// Wraps the corresponding copy function generated by `rosidl_generator_c`. - fn sequence_copy(in_seq: &crate::Sequence, out_seq: &mut crate::Sequence) -> bool; -} - -/// Trait for RMW-native messages. -/// -/// See the documentation for the [`Message`] trait, which is the trait that should generally be -/// used by user code. -/// -/// User code never needs to call this trait's method, much less implement this trait. -pub trait RmwMessage: Clone + Debug + Default + Send + Sync + Message { - /// A string representation of this message's type, e.g. "geometry_msgs/msg/Twist" - const TYPE_NAME: &'static str; - - /// Get a pointer to the correct `rosidl_message_type_support_t` structure. - fn get_type_support() -> *const std::ffi::c_void; -} - -/// Trait for types that can be used in a `rclrs::Subscription` and a `rclrs::Publisher`. -/// -/// `rosidl_generator_rs` generates two types of messages that implement this trait: -/// - An "idiomatic" message type, in the `${package_name}::msg` module -/// - An "RMW-native" message type, in the `${package_name}::msg::rmw` module -/// -/// # Idiomatic message type -/// The idiomatic message type aims to be familiar to Rust developers and ROS 2 developers coming -/// from `rclcpp`. -/// To this end, it translates the original ROS 2 message into a version that uses idiomatic Rust -/// structs: [`std::vec::Vec`] for sequences and [`std::string::String`] for strings. All other -/// fields are the same as in an RMW-native message. -/// -/// This conversion incurs some overhead when reading and publishing messages. -/// -/// It's possible to use the idiomatic type for a publisher and the RMW-native type for a -/// corresponding subscription, and vice versa. -/// -/// # RMW-native message type -/// The RMW-native message type aims to achieve higher performance by avoiding the conversion -/// step to an idiomatic message. -/// -/// It uses the following type mapping: -/// -/// | Message field type | Rust type | -/// |------------|---------------| -/// | `string` | [`String`](crate::String) | -/// | `wstring` | [`WString`](crate::WString) | -/// | `string<=N`, for example `string<=10` | [`BoundedString`](crate::BoundedString) | -/// | `wstring<=N`, for example `wstring<=10` | [`BoundedWString`](crate::BoundedWString) | -/// | `T[]`, for example `int32[]` | [`Sequence`](crate::Sequence) | -/// | `T[<=N]`, for example `int32[<=32]` | [`BoundedSequence`](crate::BoundedSequence) | -/// | `T[N]`, for example `float32[8]` | standard Rust arrays | -/// | primitive type, for example `float64` | corresponding Rust primitive type | -/// -///
-/// -/// The linked Rust types provided by this package are equivalents of types defined in C that are -/// used by the RMW layer. -/// -/// The API for these types, and the message as a whole, is still memory-safe and as convenient as -/// possible. -/// For instance, the [`Sequence`](crate::Sequence) struct that is used for sequences supports -/// iteration and all of the functionality of slices. However, it doesn't have an equivalent of -/// [`Vec::push()`], among others. -/// -/// ## What does "RMW-native" mean in detail? -/// The message can be directly passed to and from the RMW layer because (1) its layout is -/// identical to the layout of the type generated by `rosidl_generator_c` and (2) the dynamic -/// memory inside the message is owned by the C allocator. -/// -/// The above type mapping, together with a `#[repr(C)]` annotation on the message, guarantees -/// these two properties. -/// -/// This means the user of a message does not need to care about memory ownership, because that is -/// managed by the relevant functions and trait impls. -/// -/// ## I need even more detail, please -/// `rosidl_runtime_c` and the code generated by `rosidl_generator_c` manages -/// memory by means of four functions for each message: `init()`, `fini()`, `create()`, and -/// `destroy()`. -/// -/// `init()` does the following: -/// - for a message, it calls `init()` on all its members that are of non-primitive type, and applies default values -/// - for a primitive sequence, it allocates the space requested -/// - for a string, it constructs a string containing a single null terminator byte -/// - for a non-primitive sequence, it zero-allocates the space requested and calls `init()` on all its elements -/// -/// `fini()` does the following (which means after a call to `fini()`, everything inside the message has been deallocated): -/// - for a message, it calls `fini()` on all its members that are of non-primitive type -/// - for a primitive sequence, it deallocates -/// - for a string, it deallocates -/// - for a non-primitive sequence, it calls `fini()` on all its elements, and then deallocates -/// -/// `create()` simply allocates space for the message itself, and calls `init()`. -/// -/// `destroy()` simply deallocates the message itself, and calls `fini()`. -/// -/// Memory ownership by C is achieved by calling `init()` when any string or sequence is created, -/// as well as in the `Default` impl for messages. -/// -/// User code can still create messages explicitly, which will not call `init()`, but this is not a -/// problem, since nothing is allocated this way. -/// -/// The `Drop` impl for any sequence or string will call `fini()`. -pub trait Message: Clone + Debug + Default + 'static + Send + Sync { - /// The corresponding RMW-native message type. - type RmwMsg: RmwMessage; - - /// Converts the idiomatic message into an RMW-native message. - /// - /// If the idiomatic message is owned, a slightly more efficient conversion is possible. - /// This is why the function takes a `Cow`. - /// - /// If this function receives a borrowed message that is already RMW-native, it should - /// directly return that borrowed message. - /// This is why the return type is also `Cow`. - fn into_rmw_message(msg_cow: Cow<'_, Self>) -> Cow<'_, Self::RmwMsg>; - - /// Converts the RMW-native message into an idiomatic message. - fn from_rmw_message(msg: Self::RmwMsg) -> Self; -} - -/// Trait for services. -/// -/// User code never needs to call this trait's method, much less implement this trait. -pub trait Service: 'static { - /// The request message associated with this service. - type Request: Message; - - /// The response message associated with this service. - type Response: Message; - - /// Get a pointer to the correct `rosidl_service_type_support_t` structure. - fn get_type_support() -> *const std::ffi::c_void; -} - -/// Trait for actions. -/// -/// User code never needs to call this trait's method, much less implement this trait. -pub trait Action: 'static { - /// The goal message associated with this action. - type Goal: Message; - - /// The result message associated with this action. - type Result: Message; - - /// The feedback message associated with this action. - type Feedback: Message; - - /// Get a pointer to the correct `rosidl_action_type_support_t` structure. - fn get_type_support() -> *const std::ffi::c_void; -} - -/// Trait for action implementation details. -/// -/// User code never needs to implement this trait, nor use its associated types. -pub trait ActionImpl: 'static + Action { - /// The goal_status message associated with this action. - type GoalStatusMessage: Message; - - /// The feedback message associated with this action. - type FeedbackMessage: Message; - - /// The send_goal service associated with this action. - type SendGoalService: Service; - - /// The cancel_goal service associated with this action. - type CancelGoalService: Service; - - /// The get_result service associated with this action. - type GetResultService: Service; - - /// Create a goal request message with the given UUID and goal. - fn create_goal_request(goal_id: &[u8; 16], goal: RmwGoalData) -> RmwGoalRequest; - - /// Get the UUID of a goal request. - fn get_goal_request_uuid(request: &RmwGoalRequest) -> &[u8; 16]; - - /// Create a goal response message with the given acceptance and timestamp. - fn create_goal_response(accepted: bool, stamp: (i32, u32)) -> RmwGoalResponse; - - /// Get the `accepted` field of a goal response. - fn get_goal_response_accepted(response: &RmwGoalResponse) -> bool; - - /// Get the `stamp` field of a goal response. - fn get_goal_response_stamp(response: &RmwGoalResponse) -> (i32, u32); - - /// Create a feedback message with the given goal ID and contents. - fn create_feedback_message( - goal_id: &[u8; 16], - feedback: RmwFeedbackData, - ) -> RmwFeedbackMessage; - - /// Get the UUID of a feedback message. - fn get_feedback_message_uuid(feedback: &RmwFeedbackMessage) -> &[u8; 16]; - - /// Get the feedback of a feedback message. - fn get_feedback_message_feedback(feedback: &RmwFeedbackMessage) - -> &RmwFeedbackData; - - /// Create a result request message with the given goal ID. - fn create_result_request(goal_id: &[u8; 16]) -> RmwResultRequest; - - /// Get the UUID of a result request. - fn get_result_request_uuid(request: &RmwResultRequest) -> &[u8; 16]; - - /// Create a result response message with the given status and contents. - fn create_result_response(status: i8, result: RmwResultData) -> RmwResultResponse; - - /// Get the result of a result response. - fn get_result_response_result(response: &RmwResultResponse) -> &RmwResultData; - - /// Get the status of a result response. - fn get_result_response_status(response: &RmwResultResponse) -> i8; -} - -// Type definitions to simplify the ActionImpl trait -pub type RmwServiceRequest = <::Request as Message>::RmwMsg; -pub type RmwServiceResponse = <::Response as Message>::RmwMsg; -pub type RmwGoalRequest
= RmwServiceRequest<::SendGoalService>; -pub type RmwGoalResponse = RmwServiceResponse<::SendGoalService>; -pub type RmwGoalData = <::Goal as Message>::RmwMsg; -pub type RmwFeedbackData = <::Feedback as Message>::RmwMsg; -pub type RmwFeedbackMessage = <::FeedbackMessage as Message>::RmwMsg; -pub type RmwResultRequest = RmwServiceRequest<::GetResultService>; -pub type RmwResultResponse = RmwServiceResponse<::GetResultService>; -pub type RmwResultData = <::Result as Message>::RmwMsg;