ros_message/data_type.rs
1use crate::{Error, MessagePath, Result};
2use serde_derive::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::convert::TryFrom;
5use std::fmt;
6use std::fmt::Formatter;
7
8/// Enumerates all data types possible in a ROS message.
9#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(into = "String")]
11#[serde(try_from = "&str")]
12pub enum DataType {
13 /// Represents `bool`.
14 Bool,
15 /// Represents `int8` or `byte`.
16 ///
17 /// Variants are grouped to hint at the fact that they should be treated like
18 /// the same type by most code. The key exception being matching messages for
19 /// validating MD5 sums and message descriptors.
20 I8(I8Variant),
21 /// Represents `int16`.
22 I16,
23 /// Represents `int32`.
24 I32,
25 /// Represents `int64`.
26 I64,
27 /// Represents `uint8` or `char`.
28 ///
29 /// Variants are grouped to hint at the fact that they should be treated like
30 /// the same type by most code. The key exception being matching messages for
31 /// validating MD5 sums and message descriptors.
32 U8(U8Variant),
33 /// Represents `uint16`.
34 U16,
35 /// Represents `uint32`.
36 U32,
37 /// Represents `uint64`.
38 U64,
39 /// Represents `float32`.
40 F32,
41 /// Represents `float64`.
42 F64,
43 /// Represents `string`.
44 String,
45 /// Represents `time`.
46 Time,
47 /// Represents `duration`.
48 Duration,
49 /// Represents messages from same package.
50 ///
51 /// When a message in package `foo` has a field of message type `foo/Bar`, it can just
52 /// reference the field as `Bar`, and would put in this variant.
53 LocalMessage(String),
54 /// Represents messages from any package.
55 ///
56 /// When a message has a field of message type `foo/Bar`, it can
57 /// reference the field as `foo/Bar`, and would put in this variant.
58 GlobalMessage(MessagePath),
59}
60
61/// All possible names for a signed 1 byte integer in ROS messages.
62#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
63pub enum I8Variant {
64 /// Represents `int8`.
65 Int8,
66 /// Represents `byte`.
67 Byte,
68}
69
70/// All possible names for a signed 1 byte integer in ROS messages.
71#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
72pub enum U8Variant {
73 /// Represents `uint8`.
74 Uint8,
75 /// Represents `char`.
76 Char,
77}
78
79impl From<DataType> for String {
80 fn from(src: DataType) -> String {
81 format!("{}", src)
82 }
83}
84
85impl TryFrom<&str> for DataType {
86 type Error = Error;
87
88 fn try_from(src: &str) -> Result<Self> {
89 Self::parse(src)
90 }
91}
92
93impl fmt::Display for DataType {
94 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
95 match self {
96 DataType::Bool => BOOL_KEY.fmt(f),
97 DataType::I8(I8Variant::Int8) => INT8_KEY.fmt(f),
98 DataType::I8(I8Variant::Byte) => BYTE_KEY.fmt(f),
99 DataType::I16 => INT16_KEY.fmt(f),
100 DataType::I32 => INT32_KEY.fmt(f),
101 DataType::I64 => INT64_KEY.fmt(f),
102 DataType::U8(U8Variant::Uint8) => UINT8_KEY.fmt(f),
103 DataType::U8(U8Variant::Char) => CHAR_KEY.fmt(f),
104 DataType::U16 => UINT16_KEY.fmt(f),
105 DataType::U32 => UINT32_KEY.fmt(f),
106 DataType::U64 => UINT64_KEY.fmt(f),
107 DataType::F32 => FLOAT32_KEY.fmt(f),
108 DataType::F64 => FLOAT64_KEY.fmt(f),
109 DataType::String => STRING_KEY.fmt(f),
110 DataType::Time => TIME_KEY.fmt(f),
111 DataType::Duration => DURATION_KEY.fmt(f),
112 DataType::LocalMessage(ref name) => name.fmt(f),
113 DataType::GlobalMessage(ref message) => message.fmt(f),
114 }
115 }
116}
117
118const BOOL_KEY: &str = "bool";
119const INT8_KEY: &str = "int8";
120const BYTE_KEY: &str = "byte";
121const INT16_KEY: &str = "int16";
122const INT32_KEY: &str = "int32";
123const INT64_KEY: &str = "int64";
124const UINT8_KEY: &str = "uint8";
125const CHAR_KEY: &str = "char";
126const UINT16_KEY: &str = "uint16";
127const UINT32_KEY: &str = "uint32";
128const UINT64_KEY: &str = "uint64";
129const FLOAT32_KEY: &str = "float32";
130const FLOAT64_KEY: &str = "float64";
131const STRING_KEY: &str = "string";
132const TIME_KEY: &str = "time";
133const DURATION_KEY: &str = "duration";
134
135impl DataType {
136 /// Parses the data type from the type provided in a ROS msg.
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// # use ros_message::{DataType, I8Variant};
142 /// # use std::convert::TryInto;
143 /// #
144 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
145 /// assert_eq!(DataType::parse("int16")?, DataType::I16);
146 /// assert_eq!(DataType::parse("float64")?, DataType::F64);
147 /// assert_eq!(DataType::parse("byte")?, DataType::I8(I8Variant::Byte));
148 /// assert_eq!(
149 /// DataType::parse("Header")?,
150 /// DataType::GlobalMessage("std_msgs/Header".try_into()?),
151 /// );
152 /// assert_eq!(
153 /// DataType::parse("Position")?,
154 /// DataType::LocalMessage("Position".into()),
155 /// );
156 /// assert_eq!(
157 /// DataType::parse("geometry_msgs/Position")?,
158 /// DataType::GlobalMessage("geometry_msgs/Position".try_into()?),
159 /// );
160 /// assert!(DataType::parse("00bad_package/Name").is_err());
161 /// assert!(DataType::parse("a/bad/type").is_err());
162 /// # Ok(())
163 /// # }
164 /// ```
165 pub fn parse(datatype: &str) -> Result<Self> {
166 Ok(match datatype {
167 BOOL_KEY => DataType::Bool,
168 INT8_KEY => DataType::I8(I8Variant::Int8),
169 BYTE_KEY => DataType::I8(I8Variant::Byte),
170 INT16_KEY => DataType::I16,
171 INT32_KEY => DataType::I32,
172 INT64_KEY => DataType::I64,
173 UINT8_KEY => DataType::U8(U8Variant::Uint8),
174 CHAR_KEY => DataType::U8(U8Variant::Char),
175 UINT16_KEY => DataType::U16,
176 UINT32_KEY => DataType::U32,
177 UINT64_KEY => DataType::U64,
178 FLOAT32_KEY => DataType::F32,
179 FLOAT64_KEY => DataType::F64,
180 STRING_KEY => DataType::String,
181 TIME_KEY => DataType::Time,
182 DURATION_KEY => DataType::Duration,
183 "Header" => DataType::GlobalMessage(MessagePath::new("std_msgs", "Header")?),
184 _ => {
185 let parts = datatype.splitn(3, '/').collect::<Vec<&str>>();
186 match parts[..] {
187 [name] => DataType::LocalMessage(name.into()),
188 [package, name] => DataType::GlobalMessage(MessagePath::new(package, name)?),
189 _ => {
190 return Err(Error::UnsupportedDataType {
191 name: datatype.into(),
192 reason: "string needs to be in `name` or `package/name` format".into(),
193 })
194 }
195 }
196 }
197 })
198 }
199
200 /// Returns true if the type is a built in type, rather than another message.
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// # use ros_message::{DataType, I8Variant};
206 /// #
207 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
208 /// assert!(DataType::parse("int16")?.is_builtin());
209 /// assert!(DataType::parse("float64")?.is_builtin());
210 /// assert!(DataType::parse("byte")?.is_builtin());
211 /// assert!(!DataType::parse("Header")?.is_builtin());
212 /// assert!(!DataType::parse("Position")?.is_builtin());
213 /// assert!(!DataType::parse("geometry_msgs/Position")?.is_builtin());
214 /// # Ok(())
215 /// # }
216 /// ```
217 pub fn is_builtin(&self) -> bool {
218 match *self {
219 DataType::Bool
220 | DataType::I8(_)
221 | DataType::I16
222 | DataType::I32
223 | DataType::I64
224 | DataType::U8(_)
225 | DataType::U16
226 | DataType::U32
227 | DataType::U64
228 | DataType::F32
229 | DataType::F64
230 | DataType::String
231 | DataType::Time
232 | DataType::Duration => true,
233 DataType::LocalMessage(_) | DataType::GlobalMessage(_) => false,
234 }
235 }
236
237 /// Returns the representation of the data type when constructing the MD5 sum.
238 ///
239 /// For built in types, it is the same as the data type name.
240 ///
241 /// For message types, it is that message's MD5 sum, which is passed in via the `hashes`
242 /// argument.
243 ///
244 /// The `package` argument should be the package that the current message is in, to resolve
245 /// global paths of local message dependencies.
246 ///
247 /// # Errors
248 ///
249 /// An error will be returned if a message we depend upon is missing.
250 ///
251 /// # Examples
252 ///
253 /// ```
254 /// # use ros_message::{DataType, I8Variant};
255 /// # use std::convert::TryInto;
256 /// # use std::collections::HashMap;
257 /// #
258 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
259 /// let mut hashes = HashMap::new();
260 /// hashes.insert("foo/Header".try_into()?, "wrong_header".into());
261 /// hashes.insert("std_msgs/Header".try_into()?, "123".into());
262 /// hashes.insert("geometry_msgs/Position".try_into()?, "345".into());
263 /// hashes.insert("foo/Position".try_into()?, "678".into());
264 ///
265 /// assert_eq!(DataType::parse("int16")?.md5_str("foo", &hashes)?, "int16");
266 /// assert_eq!(DataType::parse("float64")?.md5_str("foo", &hashes)?, "float64");
267 /// assert_eq!(DataType::parse("byte")?.md5_str("foo", &hashes)?, "byte");
268 /// assert_eq!(DataType::parse("Header")?.md5_str("foo", &hashes)?, "123");
269 /// assert_eq!(DataType::parse("Position")?.md5_str("foo", &hashes)?, "678");
270 /// assert_eq!(DataType::parse("geometry_msgs/Position")?.md5_str("foo", &hashes)?, "345");
271 /// assert!(DataType::parse("other_msgs/Position")?.md5_str("foo", &hashes).is_err());
272 /// # Ok(())
273 /// # }
274 /// ```
275 pub fn md5_str<'a>(
276 &self,
277 package: &str,
278 hashes: &'a HashMap<MessagePath, String>,
279 ) -> Result<&'a str> {
280 Ok(match *self {
281 DataType::Bool => BOOL_KEY,
282 DataType::I8(I8Variant::Int8) => INT8_KEY,
283 DataType::I8(I8Variant::Byte) => BYTE_KEY,
284 DataType::I16 => INT16_KEY,
285 DataType::I32 => INT32_KEY,
286 DataType::I64 => INT64_KEY,
287 DataType::U8(U8Variant::Uint8) => UINT8_KEY,
288 DataType::U8(U8Variant::Char) => CHAR_KEY,
289 DataType::U16 => UINT16_KEY,
290 DataType::U32 => UINT32_KEY,
291 DataType::U64 => UINT64_KEY,
292 DataType::F32 => FLOAT32_KEY,
293 DataType::F64 => FLOAT64_KEY,
294 DataType::String => STRING_KEY,
295 DataType::Time => TIME_KEY,
296 DataType::Duration => DURATION_KEY,
297 DataType::LocalMessage(ref name) => hashes
298 .get(&MessagePath::new(package, name)?)
299 .ok_or_else(|| Error::MessageDependencyMissing {
300 package: package.into(),
301 name: name.into(),
302 })?
303 .as_str(),
304 DataType::GlobalMessage(ref message) => hashes
305 .get(message)
306 .ok_or_else(|| Error::MessageDependencyMissing {
307 package: message.package().into(),
308 name: message.name().into(),
309 })?
310 .as_str(),
311 })
312 }
313}