ros_message/parse_msg/
mod.rs
1use crate::{Error, FieldCase, FieldInfo, Result};
2use lazy_static::lazy_static;
3use regex::Regex;
4
5static IGNORE_WHITESPACE: &str = r"\s*";
6static ANY_WHITESPACE: &str = r"\s+";
7static FIELD_TYPE: &str = r"([a-zA-Z0-9_/]+)";
8static FIELD_NAME: &str = r"([a-zA-Z][a-zA-Z0-9_]*)";
9static EMPTY_BRACKETS: &str = r"\[\s*\]";
10static NUMBER_BRACKETS: &str = r"\[\s*([0-9]+)\s*\]";
11
12#[derive(Debug, PartialEq)]
13struct FieldLine {
14 field_type: String,
15 field_name: String,
16}
17
18#[inline]
19pub fn match_lines(data: &str) -> Result<Vec<FieldInfo>> {
20 data.split('\n')
21 .filter_map(match_line)
22 .collect::<Result<_>>()
23}
24
25fn match_line(data: &str) -> Option<Result<FieldInfo>> {
26 if let Some((info, data)) = match_const_string(data.trim()) {
27 return Some(FieldInfo::new(
28 &info.field_type,
29 &info.field_name,
30 FieldCase::Const(data),
31 ));
32 }
33 let data = match strip_useless(data) {
34 Ok(v) => v,
35 Err(v) => return Some(Err(v)),
36 };
37
38 if data.is_empty() {
39 return None;
40 }
41 if let Some(info) = match_field(data) {
42 return Some(FieldInfo::new(
43 &info.field_type,
44 &info.field_name,
45 FieldCase::Unit,
46 ));
47 }
48 if let Some(info) = match_vector_field(data) {
49 return Some(FieldInfo::new(
50 &info.field_type,
51 &info.field_name,
52 FieldCase::Vector,
53 ));
54 }
55 if let Some((info, count)) = match_array_field(data) {
56 return Some(FieldInfo::new(
57 &info.field_type,
58 &info.field_name,
59 FieldCase::Array(count),
60 ));
61 }
62 if let Some((info, data)) = match_const_numeric(data) {
63 return Some(FieldInfo::new(
64 &info.field_type,
65 &info.field_name,
66 FieldCase::Const(data),
67 ));
68 }
69 Some(Err(Error::BadMessageContent(data.into())))
70}
71
72fn match_const_string(data: &str) -> Option<(FieldLine, String)> {
73 lazy_static! {
74 static ref MATCHER: String = format!(
75 r"^(string){}{}{}={}(.*)$",
76 ANY_WHITESPACE, FIELD_NAME, IGNORE_WHITESPACE, IGNORE_WHITESPACE
77 );
78 static ref RE: Regex = Regex::new(&MATCHER).unwrap();
79 }
80 let captures = match RE.captures(data) {
81 Some(v) => v,
82 None => return None,
83 };
84 Some((
85 FieldLine {
86 field_type: captures.get(1).unwrap().as_str().into(),
87 field_name: captures.get(2).unwrap().as_str().into(),
88 },
89 captures.get(3).unwrap().as_str().into(),
90 ))
91}
92
93fn match_field(data: &str) -> Option<FieldLine> {
94 lazy_static! {
95 static ref MATCHER: String = format!("^{}{}{}$", FIELD_TYPE, ANY_WHITESPACE, FIELD_NAME);
96 static ref RE: Regex = Regex::new(&MATCHER).unwrap();
97 }
98 let captures = match RE.captures(data) {
99 Some(v) => v,
100 None => return None,
101 };
102 Some(FieldLine {
103 field_type: captures.get(1).unwrap().as_str().into(),
104 field_name: captures.get(2).unwrap().as_str().into(),
105 })
106}
107
108fn match_vector_field(data: &str) -> Option<FieldLine> {
109 lazy_static! {
110 static ref MATCHER: String = format!(
111 "^{}{}{}{}{}$",
112 FIELD_TYPE, IGNORE_WHITESPACE, EMPTY_BRACKETS, ANY_WHITESPACE, FIELD_NAME
113 );
114 static ref RE: Regex = Regex::new(&MATCHER).unwrap();
115 }
116 let captures = match RE.captures(data) {
117 Some(v) => v,
118 None => return None,
119 };
120 Some(FieldLine {
121 field_type: captures.get(1).unwrap().as_str().into(),
122 field_name: captures.get(2).unwrap().as_str().into(),
123 })
124}
125
126fn match_array_field(data: &str) -> Option<(FieldLine, usize)> {
127 lazy_static! {
128 static ref MATCHER: String = format!(
129 "^{}{}{}{}{}$",
130 FIELD_TYPE, IGNORE_WHITESPACE, NUMBER_BRACKETS, ANY_WHITESPACE, FIELD_NAME
131 );
132 static ref RE: Regex = Regex::new(&MATCHER).unwrap();
133 }
134 let captures = match RE.captures(data) {
135 Some(v) => v,
136 None => return None,
137 };
138 Some((
139 FieldLine {
140 field_type: captures.get(1).unwrap().as_str().into(),
141 field_name: captures.get(3).unwrap().as_str().into(),
142 },
143 captures.get(2).unwrap().as_str().parse().unwrap(),
144 ))
145}
146
147fn match_const_numeric(data: &str) -> Option<(FieldLine, String)> {
148 lazy_static! {
149 static ref MATCHER: String = format!(
150 r"^{}{}{}{}={}(-?[0-9\.eE\+\-]+)$",
151 FIELD_TYPE, ANY_WHITESPACE, FIELD_NAME, IGNORE_WHITESPACE, IGNORE_WHITESPACE
152 );
153 static ref RE: Regex = Regex::new(&MATCHER).unwrap();
154 }
155 let captures = match RE.captures(data) {
156 Some(v) => v,
157 None => return None,
158 };
159 Some((
160 FieldLine {
161 field_type: captures.get(1).unwrap().as_str().into(),
162 field_name: captures.get(2).unwrap().as_str().into(),
163 },
164 captures.get(3).unwrap().as_str().into(),
165 ))
166}
167
168#[inline]
169fn strip_useless(data: &str) -> Result<&str> {
170 Ok(data
171 .split('#')
172 .next()
173 .ok_or_else(|| Error::BadMessageContent(data.into()))?
174 .trim())
175}
176
177#[cfg(test)]
178mod tests;