@@ -162,6 +162,17 @@ impl Reflection {
162162 !schema_text. trim( ) . is_empty( ) ,
163163 "message definition is empty"
164164 ) ;
165+
166+ // rosbag2 writes a `.msg` with its top-level block first and a separator line only
167+ // before each dependency. A service or action definition is delimited from its
168+ // first line, and parsed as a `.msg` it describes the request alone.
169+ anyhow:: ensure!(
170+ !schema_text
171+ . lines( )
172+ . find( |line| !line. trim( ) . is_empty( ) )
173+ . is_some_and( re_ros_msg:: is_schema_separator) ,
174+ "definition is a delimited interface (.srv or .action), not a .msg"
175+ ) ;
165176 let schema = MessageSchema :: parse ( type_name, schema_text)
166177 . context ( "failed to parse the ROS 2 message definition" ) ?;
167178 let plan = Arc :: new (
@@ -833,6 +844,24 @@ mod tests {
833844 assert_eq ! ( forms( & chunks[ 0 ] ) , ( Some ( 1 ) , None ) ) ;
834845 }
835846
847+ #[ test]
848+ fn a_service_event_topic_is_kept_raw ( ) {
849+ let mut pipeline = pipeline_for (
850+ & [ Representation :: Lenses ] ,
851+ "test_msgs/srv/BasicTypes_Event" ,
852+ b"================================================================================\n \
853+ SRV: test_msgs/srv/BasicTypes\n \
854+ bool request_value\n \
855+ ---\n \
856+ bool response_value\n ",
857+ ) ;
858+ assert ! ( pipeline. is_replayable( ) ) ;
859+ assert_eq ! ( pipeline. representations( ) , & reps( & [ Representation :: Raw ] ) ) ;
860+ let chunks = run ( & mut pipeline, & strings ( & [ "x" ] ) ) ;
861+ assert_eq ! ( chunks. len( ) , 1 ) ;
862+ assert_eq ! ( forms( & chunks[ 0 ] ) , ( Some ( 1 ) , None ) ) ;
863+ }
864+
836865 #[ test]
837866 fn lenses_derive_archetypes_and_keep_the_struct_only_when_asked ( ) {
838867 // A lens matches `std_msgs/msg/String`, so lenses alone leave nothing to replay.
0 commit comments