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}