1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(improper_ctypes)]
5#![allow(dead_code)]
6#![allow(clippy::all)]
7
8#[cfg(feature = "doc-only")]
9include!(concat!(env!("OUT_DIR"), "/msg_bindings_doc_only.rs"));
10
11#[cfg(not(feature = "doc-only"))]
12include!(concat!(env!("OUT_DIR"), "/msg_bindings.rs"));
13
14include!(concat!(env!("OUT_DIR"), "/introspection_functions.rs"));
15include!(concat!(env!("OUT_DIR"), "/constants.rs"));
16
17mod introspection;
18
19#[cfg(not(feature = "doc-only"))]
20use {
21 crate::introspection::{MemberType, MessageMember},
22 rayon::prelude::*,
23 std::mem,
24};
25
26use quote::{format_ident, quote};
27use r2r_common::RosMsg;
28use r2r_rcl::*;
29
30use std::{borrow::Cow, ffi::CStr};
31
32use std::slice;
33
34fn rust_mangle<'a>(name: &'a str) -> Cow<'a, str> {
37 if name.contains('@')
38 || name.contains('?')
39 || name.contains('$')
40 || matches!(
41 name,
42 "abstract"
43 | "alignof"
44 | "as"
45 | "async"
46 | "await"
47 | "become"
48 | "box"
49 | "break"
50 | "const"
51 | "continue"
52 | "crate"
53 | "do"
54 | "dyn"
55 | "else"
56 | "enum"
57 | "extern"
58 | "false"
59 | "final"
60 | "fn"
61 | "for"
62 | "if"
63 | "impl"
64 | "in"
65 | "let"
66 | "loop"
67 | "macro"
68 | "match"
69 | "mod"
70 | "move"
71 | "mut"
72 | "offsetof"
73 | "override"
74 | "priv"
75 | "proc"
76 | "pub"
77 | "pure"
78 | "ref"
79 | "return"
80 | "Self"
81 | "self"
82 | "sizeof"
83 | "static"
84 | "struct"
85 | "super"
86 | "trait"
87 | "true"
88 | "try"
89 | "type"
90 | "typeof"
91 | "unsafe"
92 | "unsized"
93 | "use"
94 | "virtual"
95 | "where"
96 | "while"
97 | "yield"
98 | "str"
99 | "bool"
100 | "f32"
101 | "f64"
102 | "usize"
103 | "isize"
104 | "u128"
105 | "i128"
106 | "u64"
107 | "i64"
108 | "u32"
109 | "i32"
110 | "u16"
111 | "i16"
112 | "u8"
113 | "i8"
114 | "_"
115 )
116 {
117 let mut s = name.to_owned();
118 s = s.replace('@', "_");
119 s = s.replace('?', "_");
120 s = s.replace('$', "_");
121 s.push('_');
122 return Cow::Owned(s);
123 }
124 Cow::Borrowed(name)
125}
126
127unsafe fn introspection<'a>(
128 ptr: *const rosidl_message_type_support_t,
129) -> (
130 String,
131 String,
132 String,
133 String,
134 &'a [rosidl_typesupport_introspection_c__MessageMember],
135) {
136 let members = (*ptr).data as *const rosidl_typesupport_introspection_c__MessageMembers;
137 let namespace = CStr::from_ptr((*members).message_namespace_)
138 .to_str()
139 .unwrap();
140 let name = CStr::from_ptr((*members).message_name_).to_str().unwrap();
141 let nn: Vec<&str> = namespace.split("__").into_iter().take(2).collect();
142 let (module, prefix) = (nn[0], nn[1]);
143 let c_struct = format!(
144 "{module}__{prefix}__{msgname}",
145 module = module,
146 prefix = prefix,
147 msgname = name
148 );
149 let memberslice = slice::from_raw_parts((*members).members_, (*members).member_count_ as usize);
150 (module.to_owned(), prefix.to_owned(), name.to_owned(), c_struct, memberslice)
151}
152
153#[cfg(not(feature = "doc-only"))]
154pub fn generate_rust_service(
155 module_: &str, prefix_: &str, name_: &str,
156) -> proc_macro2::TokenStream {
157 let ident = format_ident!(
158 "rosidl_typesupport_c__\
159 get_service_type_support_handle__\
160 {module_}__\
161 {prefix_}__\
162 {name_}"
163 );
164
165 quote!(
166 #[derive(Clone,Debug,PartialEq,Serialize,Deserialize)]
167 pub struct Service();
168 impl WrappedServiceTypeSupport for Service {
169 type Request = Request;
170 type Response = Response;
171
172 fn get_ts() -> &'static rosidl_service_type_support_t {
173 unsafe {
174 &* #ident ()
175 }
176 }
177 }
178
179 )
180}
181
182#[cfg(not(feature = "doc-only"))]
183pub fn generate_rust_action(module_: &str, prefix_: &str, name_: &str) -> proc_macro2::TokenStream {
184 let ident = format_ident!(
185 "rosidl_typesupport_c__\
186 get_action_type_support_handle__\
187 {module_}__\
188 {prefix_}__\
189 {name_}"
190 );
191
192 quote! {
193 #[derive(Clone,Debug,PartialEq,Serialize,Deserialize)]
194 pub struct Action();
195 impl WrappedActionTypeSupport for Action {
196 type Goal = Goal;
197 type Result = Result;
198 type Feedback = Feedback;
199
200 type FeedbackMessage = FeedbackMessage;
202 type SendGoal = SendGoal::Service;
203 type GetResult = GetResult::Service;
204
205 fn get_ts() -> &'static rosidl_action_type_support_t {
206 unsafe {
207 &* #ident ()
208 }
209 }
210
211 fn make_goal_request_msg(goal_id: unique_identifier_msgs::msg::UUID, goal: Goal) -> SendGoal::Request {
212 SendGoal::Request {
213 goal_id,
214 goal
215 }
216 }
217
218 fn make_goal_response_msg(accepted: bool, stamp: builtin_interfaces::msg::Time) -> SendGoal::Response {
219 SendGoal::Response {
220 accepted,
221 stamp
222 }
223 }
224
225 fn make_feedback_msg(goal_id: unique_identifier_msgs::msg::UUID, feedback: Feedback) -> FeedbackMessage {
226 FeedbackMessage {
227 goal_id,
228 feedback
229 }
230 }
231
232 fn make_result_request_msg(goal_id: unique_identifier_msgs::msg::UUID) -> GetResult::Request {
233 GetResult::Request {
234 goal_id,
235 }
236 }
237
238 fn make_result_response_msg(status: i8, result: Result) -> GetResult::Response {
239 GetResult::Response {
240 status,
241 result,
242 }
243 }
244
245 fn destructure_goal_request_msg(msg: SendGoal::Request) -> (unique_identifier_msgs::msg::UUID, Goal) {
246 (msg.goal_id, msg.goal)
247 }
248
249 fn destructure_goal_response_msg(msg: SendGoal::Response) -> (bool, builtin_interfaces::msg::Time) {
250 (msg.accepted, msg.stamp)
251 }
252
253 fn destructure_feedback_msg(msg: FeedbackMessage) -> (unique_identifier_msgs::msg::UUID, Feedback) {
254 (msg.goal_id, msg.feedback)
255 }
256
257 fn destructure_result_response_msg(msg: GetResult::Response) -> (i8, Result) {
258 (msg.status, msg.result)
259 }
260
261 fn destructure_result_request_msg(msg: GetResult::Request) -> unique_identifier_msgs::msg::UUID {
262 msg.goal_id
263 }
264 }
265 }
266}
267
268#[cfg(not(feature = "doc-only"))]
269pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> proc_macro2::TokenStream {
270 let key = format!("{}__{}__{}", module_, prefix_, name_);
271 let func = *INTROSPECTION_FNS
272 .get(key.as_str())
273 .unwrap_or_else(|| panic!("code generation error: {}", key));
274 let ptr = unsafe { func() };
275
276 let (module, prefix, name, c_struct, c_members) = unsafe { introspection(ptr) };
277 assert_eq!(module, module_);
279 assert_eq!(prefix, prefix_);
280 assert_eq!(name, name_);
281
282 let name = if ["srv", "action"].contains(&prefix.as_str()) {
283 let nn: Vec<&str> = name.splitn(3, '_').collect();
289 if let [_mod_name, _srv_name, msg_name] = &nn[..] {
290 msg_name.to_string()
291 } else if let [_mod_name, msg_name] = &nn[..] {
292 msg_name.to_string()
293 } else {
294 panic!("malformed service name {}", name);
295 }
296 } else {
297 name
298 };
299
300 let name = format_ident!("{name}");
303 let c_struct = format_ident!("{c_struct}");
304
305 let fields = {
306 let fields: Vec<_> = c_members
307 .into_iter()
308 .filter_map(|c_member| {
309 let member: &MessageMember = unsafe { mem::transmute(c_member) };
310
311 let actual_field_name = member.name();
312 let field_name = member.rust_name();
313 let got_mangled = field_name != actual_field_name;
314 if field_name == "structure_needs_at_least_one_member" {
315 return None;
317 }
318
319 let field_name = format_ident!("{field_name}");
320
321 let rust_field_type = member.type_id();
322 let rust_field_type = if rust_field_type == MemberType::Message {
323 let (module, prefix, name, _, _) = unsafe { introspection(c_member.members_) };
324 let module = format_ident!("{module}");
325 let prefix = format_ident!("{prefix}");
326
327 if prefix == "action" {
329 if let Some((n1, n2)) = name.rsplit_once("_") {
330 let n1 = format_ident!("{n1}");
331 let n2 = format_ident!("{n2}");
332 quote! { #module :: #prefix :: #n1 :: #n2 }
333 } else {
334 let name = format_ident!("{name}");
335 quote! { #module :: #prefix :: #name }
336 }
337 } else {
338 let name = format_ident!("{name}");
339 quote! { #module :: #prefix :: #name }
340 }
341 } else {
342 rust_field_type.to_rust_type()
343 };
344
345 let field = if c_member.is_array_ {
346 quote! { pub #field_name : Vec< #rust_field_type > }
353 } else {
355 quote! { pub #field_name : #rust_field_type }
356 };
357
358 let attr = got_mangled.then(|| {
359 quote! { #[serde(rename = #actual_field_name )] }
360 });
361
362 Some(quote! {
363 #attr
364 #field
365 })
366 })
367 .collect();
368 quote! { #(#fields),* }
369 };
370
371 let from_native = {
372 let fields = c_members.into_iter().filter_map(|c_member| {
373 let member: &MessageMember = unsafe { mem::transmute(c_member) };
374 let field_name = member.rust_name();
375 if field_name == "structure_needs_at_least_one_member" {
376 return None;
378 }
379 let field_name = format_ident!("{field_name}");
380 let rust_field_type = member.type_id();
381 let array_info = member.array_info();
382
383 let fields = if let Some(array_info) = array_info {
384 if array_info.size > 0 && !array_info.is_upper_bound {
385 let fields1 = quote! {
389 };
392
393 let field2 = match rust_field_type {
394 MemberType::Message => {
395 let (module, prefix, name, _, _) =
396 unsafe { introspection(c_member.members_) };
397 let module = format_ident!("{module}");
398 let prefix = format_ident!("{prefix}");
399 let name = format_ident!("{name}");
400
401 quote! {
402 #field_name: {
403 let vec: Vec<_> = msg
404 .#field_name
405 .iter()
406 .map(|s| #module :: #prefix :: #name :: from_native(s))
407 .collect();
408 vec
409 },
410 }
411 }
412 MemberType::String | MemberType::WString => {
413 quote! {
414 #field_name :
415 msg. #field_name .iter().map(|s|s.to_str().to_owned()).collect(),
416 }
417 }
418 _ => {
419 quote! {
420 #field_name : msg. #field_name .to_vec(),
421 }
422 }
423 };
424
425 quote! {
426 #fields1
427 #field2
428 }
429 } else {
430 let fields1 = quote! {
435 };
438
439 let field2 = if rust_field_type == MemberType::Message {
440 let (module, prefix, name, _, _) =
441 unsafe { introspection(c_member.members_) };
442 let module = format_ident!("{module}");
443 let prefix = format_ident!("{prefix}");
444 let name = format_ident!("{name}");
445
446 quote! {
447 #field_name : {
448 let mut temp = Vec::with_capacity(msg. #field_name .size);
449 if msg. #field_name .data != std::ptr::null_mut() {
450 let slice = unsafe {
451 std::slice::from_raw_parts(
452 msg. #field_name .data,
453 msg. #field_name .size
454 )
455 };
456 for s in slice {
457 temp.push( #module :: #prefix :: #name :: from_native(s));
458 }
459 }
460 temp
461 },
462 }
463 } else {
464 quote! {
465 #field_name: msg. #field_name .to_vec(),
466 }
467 };
468
469 quote! {
470 #fields1
471 #field2
472 }
473 }
474 } else {
475 match rust_field_type {
476 MemberType::String | MemberType::WString => {
477 quote! {
478 #field_name: msg. #field_name .to_str().to_owned(),
479 }
480 }
481 MemberType::Message => {
482 let (module, prefix, name, _, _) =
483 unsafe { introspection(c_member.members_) };
484 let module = format_ident!("{module}");
485 let prefix = format_ident!("{prefix}");
486
487 if prefix == "action" {
489 let (srvname, msgname) =
490 name.rsplit_once("_").expect("ooops at from_native");
491 let srvname = format_ident!("{srvname}");
492 let msgname = format_ident!("{msgname}");
493
494 quote! {
495 #field_name:
496 #module ::
497 #prefix ::
498 #srvname ::
499 #msgname ::
500 from_native(&msg. #field_name ),
501 }
502 } else {
503 let name = format_ident!("{name}");
504
505 quote! {
506 #field_name :
507 #module :: #prefix :: #name ::from_native(&msg. #field_name),
508 }
509 }
510 }
511 _ => {
512 quote! {
513 #field_name : msg. #field_name,
514 }
515 }
516 }
517 };
518
519 Some(fields)
520 });
521 quote! {
523 fn from_native(#[allow(unused)] msg: &Self::CStruct) -> #name {
524 #name {
525 #(#fields)*
526 }
527 }
528 }
529 };
530
531 let copy_to_native = {
532 let stmts = c_members.into_iter().filter_map(|c_member| {
533 let member: &MessageMember = unsafe { mem::transmute(c_member) };
534 let field_name_str = member.rust_name();
535 if field_name_str == "structure_needs_at_least_one_member" {
536 return None
538 }
539 let rust_field_type = member.type_id();
540 let array_info = member.array_info();
541 let field_name = format_ident!("{field_name_str}");
542
543 let stmts = if let Some(array_info) = array_info {
544 let array_size = array_info.size;
545
546 if array_info.size > 0 && !array_info.is_upper_bound {
547 let content = match rust_field_type {
551 MemberType::Message => {
552 quote! {
553 for (t, s) in msg. #field_name .iter_mut().zip(&self. #field_name ) {
554 s.copy_to_native(t);
555 }
556 }
557 }
558 MemberType::String | MemberType::WString => {
559 quote! {
560 for (t, s) in msg. #field_name .iter_mut().zip(&self. #field_name ) {
561 t.assign(&s);
562 }
563 }
564 }
565 _ => {
566 quote! {
567 msg. #field_name .copy_from_slice(&self. #field_name [.. #array_size ]);
568 }
569 }
570 };
571
572 quote! {
573 assert_eq!(
574 self. #field_name .len(),
575 #array_size ,
576 "Field {} is fixed size of {}!", #field_name_str, #array_size
577 );
578 #content
579 }
580 } else {
581 if rust_field_type == MemberType::Message {
584 let (_, _, _, c_struct, _) = unsafe { introspection(c_member.members_) };
585 let init_func = format_ident!("{c_struct}__Sequence__init");
586 let fini_func = format_ident!("{c_struct}__Sequence__fini");
587 let field_name = format_ident!("{field_name}");
588
589 quote! {
590 unsafe {
591 #fini_func (&mut msg. #field_name);
592 #init_func (&mut msg. #field_name , self. #field_name .len());
593
594 if msg. #field_name .data != std::ptr::null_mut() {
595 let slice = std::slice::from_raw_parts_mut(msg. #field_name .data, msg. #field_name .size);
596 for (t, s) in slice.iter_mut().zip(&self. #field_name ) {
597 s.copy_to_native(t);
598 }
599 }
600 }
601 }
602 } else {
603 let assert_stmt = array_info.is_upper_bound.then(|| {
605 quote! {
606 assert!(
607 self. #field_name .len() <= #array_size,
608 "Field {} is upper bounded by {}!",
609 #field_name_str, #array_size
610 );
611 }
612 });
613
614 quote! {
615 #assert_stmt
616 msg. #field_name .update(&self. #field_name );
617 }
618 }
619 }
620 } else {
621 match rust_field_type {
622 MemberType::String | MemberType::WString => {
623 quote! {
624 msg. #field_name .assign(&self. #field_name );
625 }
626 }
627 MemberType::Message => {
628 quote! {
629 self. #field_name .copy_to_native(&mut msg. #field_name );
630 }
631 }
632 _ => {
633 quote! {
634 msg. #field_name = self. #field_name;
635 }
636 }
637 }
638 };
639
640 Some(stmts)
641 });
642
643 quote! {
644 fn copy_to_native(&self, #[allow(unused)] msg: &mut Self::CStruct) {
645 #(#stmts)*
646 }
647 }
648 };
649
650 let typesupport = {
651 let type_support_handle =
652 format_ident!("rosidl_typesupport_c__get_message_type_support_handle__{c_struct}");
653 let create_func = format_ident!("{c_struct}__create");
654 let destroy_func = format_ident!("{c_struct}__destroy");
655
656 quote! {
657 impl WrappedTypesupport for #name {
658 type CStruct = #c_struct;
659
660 fn get_ts() -> &'static rosidl_message_type_support_t {
661 unsafe {
662 &* #type_support_handle()
663 }
664 }
665
666 fn create_msg() -> *mut #c_struct {
667 #[cfg(not(feature = "doc-only"))]
668 unsafe {
669 #create_func ()
670 }
671 #[cfg(feature = "doc-only")]
672 #create_func ()
673 }
674
675 fn destroy_msg(msg: *mut #c_struct) -> () {
676 #[cfg(not(feature = "doc-only"))]
677 unsafe {
678 #destroy_func (msg)
679 };
680 #[cfg(feature = "doc-only")]
681 #destroy_func (msg)
682 }
683
684 #from_native
685 #copy_to_native
686 }
687 }
688 };
689
690 let impl_default = quote! {
691 impl Default for #name {
692 fn default() -> Self {
693 let msg_native = WrappedNativeMsg::< #name >::new();
694 #name :: from_native(&msg_native)
695 }
696 }
697 };
698
699 let constant_items: Vec<_> = CONSTANTS_MAP
700 .get(&key)
701 .cloned()
702 .into_iter()
703 .flatten()
704 .map(|(const_name, typ)| {
705 let const_name = format_ident!("{const_name}");
706 let value = format_ident!("{key}__{const_name}");
707 if let Ok(mut typ) = syn::parse_str::<Box<syn::TypeReference>>(typ) {
708 typ.lifetime = Some(syn::Lifetime::new("'static", proc_macro2::Span::call_site()));
711 quote! { pub const #const_name: #typ = #value; }
712 } else if let Ok(typ) = syn::parse_str::<Box<syn::Type>>(typ) {
713 quote! { pub const #const_name: #typ = #value; }
715 } else {
716 quote! { pub const #const_name: #typ = #value; }
718 }
719 })
720 .collect();
721
722 let impl_constants = if constant_items.is_empty() {
723 quote! {}
724 } else {
725 quote! {
726 #[allow(non_upper_case_globals)]
727 impl #name {
728 #(#constant_items)*
729 }
730 }
731 };
732
733 quote! {
734 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
735 #[serde(default)]
736 pub struct #name {
737 #fields
738 }
739
740 #typesupport
741 #impl_default
742 #impl_constants
743 }
744}
745
746#[cfg(not(feature = "doc-only"))]
747pub fn generate_untyped_helper(msgs: &[RosMsg]) -> proc_macro2::TokenStream {
748 let (funcs, entries): (Vec<_>, Vec<_>) = msgs
749 .into_par_iter()
750 .filter(|msg| !["srv", "action"].contains(&msg.prefix.as_str()))
751 .map(|msg| {
752 let RosMsg {
753 module,
754 prefix,
755 name,
756 } = msg;
757 let typename = type_name(&msg);
758 let rustname = {
759 let module = format_ident!("{module}");
760 let prefix = format_ident!("{prefix}");
761 let name = format_ident!("{name}");
762 quote! { #module :: #prefix :: #name }
763 };
764
765 let func_ident =
766 format_ident!("new_wrapped_native_msg_untyped_{module}_{prefix}_{name}");
767 let func = quote! {
768 #[allow(non_snake_case)]
769 fn #func_ident() -> WrappedNativeMsgUntyped {
770 WrappedNativeMsgUntyped::new::< #rustname >()
771 }
772 };
773 let entry = quote! { #typename => #func_ident };
774
775 (unsafe { force_send(func) }, unsafe { force_send(entry) })
776 })
777 .unzip();
778 let funcs = funcs.into_iter().map(|tokens| tokens.unwrap());
779 let entries = entries.into_iter().map(|tokens| tokens.unwrap());
780
781 quote! {
782 impl WrappedNativeMsgUntyped {
783 pub fn new_from(typename: &str) -> Result<Self> {
784 #(#funcs)*
785
786 static MAP: phf::Map<&'static str, fn() -> WrappedNativeMsgUntyped> = phf::phf_map! { #(#entries),* };
787
788 let func = MAP.get(typename)
789 .ok_or_else(|| Error::InvalidMessageType{ msgtype: typename.into() })?;
790 Ok(func())
791 }
792 }
793 }
794}
795
796#[cfg(not(feature = "doc-only"))]
797pub fn generate_untyped_service_helper(msgs: &[RosMsg]) -> proc_macro2::TokenStream {
798 let (funcs, entries): (Vec<_>, Vec<_>) = msgs
799 .into_par_iter()
800 .filter(|msg| msg.prefix == "srv")
801 .map(|msg| {
802 let RosMsg {
803 module,
804 prefix,
805 name,
806 } = msg;
807 let typename = type_name(&msg);
808 let rustname = {
809 let module = format_ident!("{module}");
810 let prefix = format_ident!("{prefix}");
811 let name = format_ident!("{name}");
812 quote! { #module :: #prefix :: #name :: Service }
813 };
814 let func_ident = format_ident!("new_untyped_service_support_{module}_{prefix}_{name}");
815
816 let func = quote! {
817 #[allow(non_snake_case)]
818 fn #func_ident() -> UntypedServiceSupport {
819 UntypedServiceSupport::new::< #rustname >()
820 }
821 };
822 let entry = quote! { #typename => #func_ident };
823
824 unsafe { (force_send(func), force_send(entry)) }
825 })
826 .unzip();
827 let funcs = funcs.into_iter().map(|tokens| tokens.unwrap());
828 let entries = entries.into_iter().map(|tokens| tokens.unwrap());
829
830 quote! {
831 impl UntypedServiceSupport {
832 pub fn new_from(typename: &str) -> Result<Self> {
833 #(#funcs)*
834
835 static MAP: phf::Map<&'static str, fn() -> UntypedServiceSupport> = phf::phf_map! { #(#entries),* };
836
837 let func = MAP.get(typename)
838 .ok_or_else(|| Error::InvalidMessageType{ msgtype: typename.into() })?;
839 Ok(func())
840 }
841 }
842 }
843}
844
845#[cfg(not(feature = "doc-only"))]
846pub fn generate_untyped_action_helper(msgs: &[RosMsg]) -> proc_macro2::TokenStream {
847 let (funcs, entries): (Vec<_>, Vec<_>) = msgs
848 .into_par_iter()
849 .filter(|msg| msg.prefix == "action")
850 .map(|msg| {
851 let RosMsg {
852 module,
853 prefix,
854 name,
855 } = msg;
856 let typename = type_name(&msg);
857 let rustname = {
858 let module = format_ident!("{module}");
859 let prefix = format_ident!("{prefix}");
860 let name = format_ident!("{name}");
861 quote! { #module :: #prefix :: #name :: Action }
862 };
863 let func_ident = format_ident!("new_untyped_service_support_{module}_{prefix}_{name}");
864
865 let func = quote! {
866 #[allow(non_snake_case)]
867 fn #func_ident() -> UntypedActionSupport {
868 UntypedActionSupport::new::< #rustname >()
869 }
870 };
871 let entry = quote! { #typename => #func_ident };
872
873 unsafe { (force_send(func), force_send(entry)) }
874 })
875 .unzip();
876
877 let funcs = funcs.into_iter().map(|tokens| tokens.unwrap());
878 let entries = entries.into_iter().map(|tokens| tokens.unwrap());
879
880 quote! {
881 impl UntypedActionSupport {
882 pub fn new_from(typename: &str) -> Result<Self> {
883 #(#funcs)*
884
885 static MAP: phf::Map<&'static str, fn() -> UntypedActionSupport> = phf::phf_map! { #(#entries),* };
886
887 let func = MAP.get(typename)
888 .ok_or_else(|| Error::InvalidMessageType{ msgtype: typename.into() })?;
889 Ok(func())
890 }
891 }
892 }
893}
894
895fn type_name(msg: &RosMsg) -> String {
896 let RosMsg {
897 module,
898 prefix,
899 name,
900 } = msg;
901 format!("{module}/{prefix}/{name}")
902}
903
904fn rust_name(msg: &RosMsg) -> proc_macro2::TokenStream {
905 let RosMsg {
906 module,
907 prefix,
908 name,
909 } = msg;
910 let module = format_ident!("{module}");
911 let prefix = format_ident!("{prefix}");
912 let name = format_ident!("{name}");
913 quote! { #module :: #prefix :: #name :: Action }
914}
915
916unsafe fn force_send<T>(value: T) -> force_send_sync::Send<T> {
917 force_send_sync::Send::new(value)
918}