r2r_msg_gen/
introspection.rs

1use crate::{
2    rosidl_message_type_support_t as CTypeSupport,
3    rosidl_typesupport_introspection_c__MessageMember as CMessageMember,
4    rosidl_typesupport_introspection_c__MessageMembers as CMessageMembers, rust_mangle,
5};
6use quote::quote;
7use std::{
8    borrow::Cow,
9    ffi::{c_char, CStr},
10    mem, slice,
11};
12
13pub struct Introspection<'a> {
14    pub module: &'a str,
15    pub prefix: &'a str,
16    pub name: &'a str,
17    pub members: &'a [MessageMember],
18}
19
20impl<'a> Introspection<'a> {
21    pub fn c_struct_name(&self) -> String {
22        let Self {
23            module,
24            prefix,
25            name,
26            ..
27        } = *self;
28        format!("{module}__{prefix}__{name}")
29    }
30}
31
32#[repr(transparent)]
33pub struct TypeSupport(CTypeSupport);
34
35impl TypeSupport {
36    pub unsafe fn introspection(&self) -> Introspection<'_> {
37        let members = (self.0.data as *const MessageMembers).as_ref().unwrap();
38        let namespace = members.message_namespace();
39        let name = members.message_name();
40        let members = members.members();
41
42        let (module, remain) = namespace.split_once("__").unwrap();
43        let (prefix, _) = remain.split_once("__").unwrap();
44
45        Introspection {
46            module,
47            prefix,
48            name,
49            members,
50        }
51    }
52}
53
54#[repr(transparent)]
55pub struct MessageMember(CMessageMember);
56
57impl MessageMember {
58    pub fn name(&self) -> &str {
59        unsafe { ptr_to_str(self.0.name_) }
60    }
61
62    pub fn rust_name(&self) -> Cow<'_, str> {
63        rust_mangle(self.name())
64    }
65
66    pub fn type_id(&self) -> MemberType {
67        MemberType::from_type_id(self.0.type_id_).unwrap()
68    }
69
70    pub fn string_upper_bound(&self) -> Option<usize> {
71        if self.type_id() == MemberType::String {
72            Some(self.0.string_upper_bound_ as usize)
73        } else {
74            None
75        }
76    }
77
78    pub fn members(&self) -> Option<&TypeSupport> {
79        if self.type_id() != MemberType::Message {
80            return None;
81        }
82
83        unsafe {
84            let ptr = self.0.members_ as *const TypeSupport;
85            let ref_ = ptr.as_ref().unwrap();
86            Some(ref_)
87        }
88    }
89
90    pub fn is_array(&self) -> bool {
91        self.0.is_array_
92    }
93
94    pub fn array_size(&self) -> Option<usize> {
95        if self.is_array() {
96            Some(self.0.array_size_)
97        } else {
98            None
99        }
100    }
101
102    pub fn is_upper_bound(&self) -> Option<bool> {
103        if self.is_array() {
104            Some(self.0.is_upper_bound_)
105        } else {
106            None
107        }
108    }
109
110    pub fn array_info(&self) -> Option<ArrayInfo> {
111        self.0.is_array_.then(|| ArrayInfo {
112            size: self.0.array_size_,
113            is_upper_bound: self.0.is_upper_bound_,
114        })
115    }
116
117    pub fn offset(&self) -> usize {
118        self.0.offset_ as usize
119    }
120}
121
122#[repr(transparent)]
123pub struct MessageMembers(CMessageMembers);
124
125impl MessageMembers {
126    pub fn message_namespace(&self) -> &str {
127        unsafe { ptr_to_str(self.0.message_namespace_) }
128    }
129
130    pub fn message_name(&self) -> &str {
131        unsafe { ptr_to_str(self.0.message_name_) }
132    }
133
134    pub fn member_count(&self) -> usize {
135        self.0.member_count_ as usize
136    }
137
138    pub fn size_of(&self) -> usize {
139        self.0.size_of_ as usize
140    }
141
142    pub fn members(&self) -> &[MessageMember] {
143        unsafe {
144            let members: &[CMessageMember] =
145                slice::from_raw_parts(self.0.members_, self.member_count());
146            mem::transmute(members)
147        }
148    }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152pub enum MemberType {
153    Bool,
154    I8,
155    I16,
156    I32,
157    I64,
158    U8,
159    U16,
160    U32,
161    U64,
162    U128,
163    F32,
164    F64,
165    Char,
166    WChar,
167    String,
168    WString,
169    Message,
170}
171
172impl MemberType {
173    /// Get the enum variant for the type id.
174    ///
175    /// Because the c enum has been named between galactic and the
176    /// next release, we cannot know its name. therefor we use the
177    /// constants as is and hope we notice when they change.
178    ///
179    /// ```ignore
180    /// rosidl_typesupport_introspection_c__ROS_TYPE_FLOAT = 1,
181    /// rosidl_typesupport_introspection_c__ROS_TYPE_DOUBLE = 2,
182    /// rosidl_typesupport_introspection_c__ROS_TYPE_LONG_DOUBLE = 3,
183    /// rosidl_typesupport_introspection_c__ROS_TYPE_CHAR = 4,
184    /// rosidl_typesupport_introspection_c__ROS_TYPE_WCHAR = 5,
185    /// rosidl_typesupport_introspection_c__ROS_TYPE_BOOLEAN = 6,
186    /// rosidl_typesupport_introspection_c__ROS_TYPE_OCTET = 7,
187    /// rosidl_typesupport_introspection_c__ROS_TYPE_UINT8 = 8,
188    /// rosidl_typesupport_introspection_c__ROS_TYPE_INT8 = 9,
189    /// rosidl_typesupport_introspection_c__ROS_TYPE_UINT16 = 10,
190    /// rosidl_typesupport_introspection_c__ROS_TYPE_INT16 = 11,
191    /// rosidl_typesupport_introspection_c__ROS_TYPE_UINT32 = 12,
192    /// rosidl_typesupport_introspection_c__ROS_TYPE_INT32 = 13,
193    /// rosidl_typesupport_introspection_c__ROS_TYPE_UINT64 = 14,
194    /// rosidl_typesupport_introspection_c__ROS_TYPE_INT64 = 15,
195    /// rosidl_typesupport_introspection_c__ROS_TYPE_STRING = 16,
196    /// rosidl_typesupport_introspection_c__ROS_TYPE_WSTRING = 17,
197    /// rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE = 18,
198    /// ```
199    pub fn from_type_id(id: u8) -> Option<Self> {
200        Some(match id {
201            1 => Self::F32,
202            2 => Self::F64,
203            3 => Self::U128,
204            4 => Self::Char,
205            5 => Self::WChar,
206            6 => Self::Bool,
207            7 | 8 => Self::U8,
208            9 => Self::I8,
209            10 => Self::U16,
210            11 => Self::I16,
211            12 => Self::U32,
212            13 => Self::I32,
213            14 => Self::U64,
214            15 => Self::I64,
215            16 => Self::String,
216            17 => Self::WString,
217            18 => Self::Message,
218            _ => return None,
219        })
220    }
221
222    pub fn to_rust_type(&self) -> proc_macro2::TokenStream {
223        match self {
224            MemberType::Bool => quote! { bool },
225            MemberType::I8 => quote! { i8 },
226            MemberType::I16 => quote! { i16 },
227            MemberType::I32 => quote! { i32 },
228            MemberType::I64 => quote! { i64 },
229            MemberType::U8 => quote! { u8 },
230            MemberType::U16 => quote! { u16 },
231            MemberType::U32 => quote! { u32 },
232            MemberType::U64 => quote! { u64 },
233            MemberType::U128 => quote! { u128 },
234            MemberType::F32 => quote! { f32 },
235            MemberType::F64 => quote! { f64 },
236            MemberType::Char => quote! { std::ffi::c_char },
237            MemberType::WChar => quote! { u16 },
238            MemberType::String => quote! { std::string::String },
239            MemberType::WString => quote! { std::string::String },
240            MemberType::Message => quote! { message },
241        }
242    }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
246pub struct ArrayInfo {
247    pub size: usize,
248    pub is_upper_bound: bool,
249}
250
251unsafe fn ptr_to_str(ptr: *const c_char) -> &'static str {
252    CStr::from_ptr(ptr).to_str().unwrap()
253}