1- # Copyright 2016-2017 Esteve Fernandez <esteve@apache.org>
1+ # Copyright 2018-2026 Esteve Fernandez <esteve@apache.org>
22#
33# Licensed under the Apache License, Version 2.0 (the "License");
44# you may not use this file except in compliance with the License.
1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ """Generate Rust code for ROS interfaces."""
16+
1517import os
1618import pathlib
1719
@@ -55,12 +57,15 @@ def _removesuffix(s, suffix):
5557
5658package_name = ""
5759
60+
5861# Taken from http://stackoverflow.com/a/6425628
5962def convert_lower_case_underscore_to_camel_case (word ):
63+ """Convert a lower-case underscore name to CamelCase."""
6064 return '' .join (x .capitalize () or '_' for x in word .split ('_' ))
6165
6266
6367def generate_rs (generator_arguments_file , typesupport_impls ):
68+ """Generate Rust code from rosidl generator arguments."""
6469 args = rosidl_pycommon .read_generator_arguments (generator_arguments_file )
6570
6671 global package_name
@@ -215,30 +220,38 @@ def generate_rs(generator_arguments_file, typesupport_impls):
215220
216221 return 0
217222
223+
218224def get_rs_name (name ):
225+ """Return the Rust-safe spelling of a ROS field name."""
219226 keywords = [
220227 # strict keywords
221- 'as' , 'break' , 'const' , 'continue' , 'crate' , 'else' , 'enum' , 'extern' , 'false' , 'fn' , 'for' , 'if' , 'for' ,
222- 'impl' , 'in' , 'let' , 'loop' , 'match' , 'mod' , 'move' , 'mut' , 'pub' , 'ref' , 'return' , 'self' , 'Self' , 'static' ,
223- 'struct' , 'super' , 'trait' , 'true' , 'type' , 'unsafe' , 'use' , 'where' , 'while' ,
228+ 'as' , 'break' , 'const' , 'continue' , 'crate' , 'else' , 'enum' ,
229+ 'extern' , 'false' , 'fn' , 'for' , 'if' , 'for' , 'impl' , 'in' , 'let' ,
230+ 'loop' , 'match' , 'mod' , 'move' , 'mut' , 'pub' , 'ref' , 'return' ,
231+ 'self' , 'Self' , 'static' , 'struct' , 'super' , 'trait' , 'true' ,
232+ 'type' , 'unsafe' , 'use' , 'where' , 'while' ,
224233 # Edition 2024+
225234 'gen' ,
226235 # Edition 2018+
227236 'async' , 'await' , 'dyn' ,
228237 # Reserved
229- 'abstract' , 'become' , 'box' , 'do' , 'final' , 'macro' , 'override' , 'priv' , 'typeof' , 'unsized' , 'virtual' ,
238+ 'abstract' , 'become' , 'box' , 'do' , 'final' , 'macro' , 'override' ,
239+ 'priv' , 'typeof' , 'unsized' , 'virtual' ,
230240 'yield' , 'try'
231241 ]
232242 # If the field name is a reserved keyword in Rust append an underscore
233243 return name if name not in keywords else name + '_'
234244
245+
235246def escape_string (s ):
247+ """Escape a string literal for generated Rust code."""
236248 s = s .replace ('\\ ' , '\\ \\ ' )
237249 s = s .replace ("'" , "\\ '" )
238250 return s
239251
240252
241253def value_to_rs (type_ , value ):
254+ """Convert a rosidl default value to a Rust expression."""
242255 assert type_ .is_primitive_type ()
243256 assert value is not None
244257
@@ -253,6 +266,7 @@ def value_to_rs(type_, value):
253266
254267
255268def primitive_value_to_rs (type_ , value ):
269+ """Convert a primitive rosidl value to a Rust expression."""
256270 assert type_ .is_primitive_type ()
257271 assert value is not None
258272
@@ -285,6 +299,7 @@ def primitive_value_to_rs(type_, value):
285299
286300
287301def constant_value_to_rs (type_ , value ):
302+ """Convert a rosidl constant value to a Rust expression."""
288303 assert value is not None
289304
290305 if isinstance (type_ , BasicType ):
@@ -321,14 +336,20 @@ def constant_value_to_rs(type_, value):
321336
322337
323338def pre_field_serde (type_ ):
339+ """Return serde attributes needed before a generated Rust field."""
324340 if isinstance (type_ , Array ) and type_ .size > 32 :
325- return '#[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))]\n '
341+ return (
342+ '#[cfg_attr(feature = "serde", '
343+ 'serde(with = "serde_big_array::BigArray"))]\n '
344+ )
326345 else :
327346 return ''
328347
329348
330349def make_get_rs_type (idiomatic ):
350+ """Create a function that converts rosidl types to Rust type names."""
331351 def get_rs_type (type_ , current_idiomatic , desired_idiomatic ):
352+ """Convert a rosidl type to a Rust type name."""
332353 if isinstance (type_ , BasicType ):
333354 if type_ .typename == 'boolean' :
334355 return 'bool'
@@ -359,22 +380,41 @@ def get_rs_type(type_, current_idiomatic, desired_idiomatic):
359380 elif type_ .typename == 'uint64' :
360381 return 'u64'
361382 elif isinstance (type_ , BoundedString ):
362- return 'rosidl_runtime_rs::BoundedString<{}>' .format (type_ .maximum_size )
383+ return 'rosidl_runtime_rs::BoundedString<{}>' .format (
384+ type_ .maximum_size )
363385 elif isinstance (type_ , BoundedWString ):
364- return 'rosidl_runtime_rs::BoundedWString<{}>' .format (type_ .maximum_size )
386+ return 'rosidl_runtime_rs::BoundedWString<{}>' .format (
387+ type_ .maximum_size )
365388 elif isinstance (type_ , UnboundedString ):
366- return 'std::string::String' if current_idiomatic and desired_idiomatic else 'rosidl_runtime_rs::String'
389+ if current_idiomatic and desired_idiomatic :
390+ return 'std::string::String'
391+ return 'rosidl_runtime_rs::String'
367392 elif isinstance (type_ , UnboundedWString ):
368- return 'std::string::String' if current_idiomatic and desired_idiomatic else 'rosidl_runtime_rs::WString'
393+ if current_idiomatic and desired_idiomatic :
394+ return 'std::string::String'
395+ return 'rosidl_runtime_rs::WString'
369396 elif isinstance (type_ , Array ):
370- return f'[{ get_rs_type (type_ .value_type , current_idiomatic , desired_idiomatic )} ; { type_ .size } ]'
397+ nested_type = get_rs_type (
398+ type_ .value_type , current_idiomatic , desired_idiomatic )
399+ return f'[{ nested_type } ; { type_ .size } ]'
371400 elif isinstance (type_ , UnboundedSequence ):
372- container_type = 'Vec' if current_idiomatic and desired_idiomatic else 'rosidl_runtime_rs::Sequence'
373- return f'{ container_type } <{ get_rs_type (type_ .value_type , current_idiomatic , desired_idiomatic )} >'
401+ if current_idiomatic and desired_idiomatic :
402+ container_type = 'Vec'
403+ else :
404+ container_type = 'rosidl_runtime_rs::Sequence'
405+ nested_type = get_rs_type (
406+ type_ .value_type , current_idiomatic , desired_idiomatic )
407+ return f'{ container_type } <{ nested_type } >'
374408 elif isinstance (type_ , BoundedSequence ):
375- # BoundedSequences can be in the idiomatic API, but the containing type cannot be from the
376- # idiomatic API because we do not implement SequenceAlloc for idiomatic types.
377- return f'rosidl_runtime_rs::BoundedSequence<{ get_rs_type (type_ .value_type , current_idiomatic , False )} , { type_ .maximum_size } >'
409+ # BoundedSequences can be in the idiomatic API, but the
410+ # containing type cannot be from the idiomatic API because we do
411+ # not implement SequenceAlloc for idiomatic types.
412+ nested_type = get_rs_type (
413+ type_ .value_type , current_idiomatic , False )
414+ return (
415+ 'rosidl_runtime_rs::BoundedSequence<'
416+ f'{ nested_type } , { type_ .maximum_size } >'
417+ )
378418 elif isinstance (type_ , NamespacedType ):
379419 # All types should be referencable like this
380420 # `super::msg::rmw::Foo` (From idiomatic modules)
@@ -384,8 +424,9 @@ def get_rs_type(type_, current_idiomatic, desired_idiomatic):
384424
385425 symbol = f'{ prefix } { "::" .join (type_ .namespaced_name ()[1 :])} '
386426
387- # This symbol is coming from an external crate (or needs a `use` statement).
388- # So it should not be relative (i.e., no `super::`) and should have the top level
427+ # This symbol is coming from an external crate (or needs a `use`
428+ # statement). So it should not be relative (i.e., no `super::`)
429+ # and should have the top level
389430 # package name (i.e., `builtin_interfaces::`)
390431 top_level_package = type_ .namespaces [0 ]
391432 if top_level_package != package_name :
@@ -400,6 +441,7 @@ def get_rs_type(type_, current_idiomatic, desired_idiomatic):
400441
401442 assert False , "unknown type '%s'" % type_ .typename
402443
403- # Start out by assuming all calls have matching current and desired idiomatic values.
404- # (i.e. symbols within the `...::rmw` scope want other values in the `...::rmw` scope).
444+ # Start out by assuming all calls have matching current and desired
445+ # idiomatic values. Symbols within the `...::rmw` scope want other values
446+ # in the `...::rmw` scope.
405447 return lambda _type : get_rs_type (_type , idiomatic , idiomatic )
0 commit comments