Skip to content

Commit 2c03cf5

Browse files
committed
Initial import
Signed-off-by: Esteve Fernandez <[email protected]>
1 parent 34ad1c3 commit 2c03cf5

File tree

6 files changed

+989
-0
lines changed

6 files changed

+989
-0
lines changed

Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "ros_introspection"
3+
version = "0.1.0"
4+
authors = ["Esteve Fernandez <[email protected]>"]
5+
edition = "2021"
6+
license = "Apache-2.0"
7+
description = "Parser for ROS .msg files"
8+
9+
[dependencies]
10+
ament_rs = "0.3.0"
11+
anyhow = "1.0.98"
12+
regex = "1.11.1"
13+

src/field.rs

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
use crate::r#type::Type;
2+
use anyhow::Result;
3+
use regex::Regex;
4+
use std::str::FromStr;
5+
6+
#[derive(Debug, Clone)]
7+
pub struct Field {
8+
fieldname: String,
9+
field_type: Type,
10+
is_array: bool,
11+
array_size: isize,
12+
is_constant: bool,
13+
value: String,
14+
}
15+
16+
impl Field {
17+
/// Creates a new `Field` instance with the given type and name.
18+
///
19+
/// # Arguments
20+
///
21+
/// * `field_type` - The `Type` of the field.
22+
/// * `name` - A string slice that holds the name of the field.
23+
///
24+
/// # Returns
25+
///
26+
/// * `Self` - The new `Field` instance.
27+
pub fn new_with_type(field_type: Type, name: &str) -> Self {
28+
Self {
29+
fieldname: name.to_owned(),
30+
field_type,
31+
is_array: false,
32+
array_size: 1,
33+
is_constant: false,
34+
value: String::new(),
35+
}
36+
}
37+
38+
/// Creates a new `Field` instance from a definition string.
39+
///
40+
/// # Arguments
41+
///
42+
/// * `definition` - A string slice that holds the definition of the field.
43+
///
44+
/// # Returns
45+
///
46+
/// * `Result<Self>` - A result containing the new `Field` instance or an error.
47+
///
48+
/// # Errors
49+
///
50+
/// This function will return an error if:
51+
/// - The regular expression for parsing the type, field, or array fails to compile.
52+
/// - The type, field, or array size cannot be extracted from the definition.
53+
/// - The array size is not a valid integer.
54+
pub fn new_with_definition(definition: &str) -> Result<Self> {
55+
let type_regex =
56+
Regex::new(r"[a-zA-Z][a-zA-Z0-9_]*(/[a-zA-Z][a-zA-Z0-9_]*){0,1}(\[[0-9]*\]){0,1}")?;
57+
let field_regex = Regex::new(r"[a-zA-Z][a-zA-Z0-9_]*")?;
58+
let array_regex = Regex::new(r"(.+)(\[(\d*)\])")?;
59+
60+
let mut begin = definition;
61+
62+
// Find type
63+
let mut type_ = if let Some(what) = type_regex.find(begin) {
64+
begin = &begin[what.end()..];
65+
what.as_str().to_owned()
66+
} else {
67+
return Err(anyhow::anyhow!("Bad type when parsing field: {definition}"));
68+
};
69+
70+
// Find field
71+
let fieldname = if let Some(what) = field_regex.find(begin) {
72+
begin = &begin[what.end()..];
73+
what.as_str().to_owned()
74+
} else {
75+
return Err(anyhow::anyhow!(
76+
"Bad field when parsing field: {definition}"
77+
));
78+
};
79+
80+
// Find array size
81+
// Clone type_ to avoid borrowing issues
82+
let temp_type = type_.clone();
83+
let (is_array, array_size) = if let Some(what) = array_regex.captures(&temp_type) {
84+
type_ = what[1].to_string();
85+
if what.len() == 3 {
86+
(true, -1)
87+
} else if let Some(size) = what.get(3) {
88+
let array_size = if size.as_str().is_empty() {
89+
-1
90+
} else {
91+
isize::from_str(size.as_str())?
92+
};
93+
(true, array_size)
94+
} else {
95+
(true, -1)
96+
}
97+
} else {
98+
(false, 1)
99+
};
100+
101+
// Find if constant or comment
102+
let (is_constant, value) = if let Some(what) = Regex::new(r"\S")?.find(begin) {
103+
if what.as_str() == "=" {
104+
begin = &begin[what.end()..];
105+
// Copy constant
106+
let value = if type_ == "string" {
107+
begin.to_owned()
108+
} else if let Some(what) = Regex::new(r"\s*#")?.find(begin) {
109+
begin[..what.start()].to_string()
110+
} else {
111+
begin.to_owned()
112+
}
113+
.trim()
114+
.to_owned();
115+
(true, value)
116+
} else if what.as_str() == "#" {
117+
// Ignore comment
118+
(false, String::default())
119+
} else {
120+
let value = if let Some(what) = Regex::new(r"\s*#")?.find(begin) {
121+
begin[..what.start()].to_string()
122+
} else {
123+
begin.to_owned()
124+
};
125+
(false, value)
126+
}
127+
} else {
128+
(false, String::default())
129+
};
130+
131+
Ok(Self {
132+
fieldname,
133+
field_type: Type::new(type_.as_str())?,
134+
is_array,
135+
array_size,
136+
is_constant,
137+
value,
138+
})
139+
}
140+
141+
/// Returns a reference to the type of the field.
142+
///
143+
/// # Returns
144+
///
145+
/// * `&Type` - A reference to the `Type` of the field.
146+
pub fn type_(&self) -> &Type {
147+
&self.field_type
148+
}
149+
150+
/// Changes the type of the field.
151+
///
152+
/// # Arguments
153+
///
154+
/// * `new_type` - The new `Type` of the field.
155+
pub fn change_type(&mut self, new_type: Type) {
156+
self.field_type = new_type;
157+
}
158+
159+
/// Returns the name of the field.
160+
///
161+
/// # Returns
162+
///
163+
/// * `&str` - A string slice that holds the name of the field.
164+
pub fn name(&self) -> &str {
165+
&self.fieldname
166+
}
167+
168+
/// Returns whether the field is an array.
169+
///
170+
/// # Returns
171+
///
172+
/// * `bool` - `true` if the field is an array, `false` otherwise.
173+
pub fn is_array(&self) -> bool {
174+
self.is_array
175+
}
176+
177+
/// Returns whether the field is a constant.
178+
///
179+
/// # Returns
180+
///
181+
/// * `bool` - `true` if the field is a constant, `false` otherwise.
182+
pub fn is_constant(&self) -> bool {
183+
self.is_constant
184+
}
185+
186+
/// Returns the array size of the field.
187+
///
188+
/// # Returns
189+
///
190+
/// * `isize` - The array size of the field.
191+
pub fn array_size(&self) -> isize {
192+
self.array_size
193+
}
194+
195+
/// Returns the value of the field.
196+
///
197+
/// # Returns
198+
///
199+
/// * `&str` - A string slice that holds the value of the field.
200+
pub fn value(&self) -> &str {
201+
&self.value
202+
}
203+
}
204+
205+
#[cfg(test)]
206+
mod tests {
207+
use super::*;
208+
use crate::field::Type;
209+
210+
#[test]
211+
fn test_new_with_type() {
212+
let field_type = Type::new("int32").unwrap();
213+
let field = Field::new_with_type(field_type.clone(), "test_field");
214+
215+
assert_eq!(field.fieldname, "test_field");
216+
assert_eq!(field.field_type, field_type);
217+
assert!(!field.is_array);
218+
assert_eq!(field.array_size, 1);
219+
assert!(!field.is_constant);
220+
assert_eq!(field.value, "");
221+
}
222+
223+
#[test]
224+
fn test_new_with_definition() {
225+
let field = Field::new_with_definition("int32 test_field").unwrap();
226+
assert_eq!(field.fieldname, "test_field");
227+
assert_eq!(field.field_type, Type::new("int32").unwrap());
228+
assert!(!field.is_array);
229+
assert_eq!(field.array_size, 1);
230+
assert!(!field.is_constant);
231+
assert_eq!(field.value, "");
232+
233+
let field = Field::new_with_definition("string[10] test_array").unwrap();
234+
assert_eq!(field.fieldname, "test_array");
235+
assert_eq!(field.field_type, Type::new("string").unwrap());
236+
assert!(field.is_array);
237+
assert_eq!(field.array_size, 10);
238+
assert!(!field.is_constant);
239+
assert_eq!(field.value, "");
240+
241+
let field = Field::new_with_definition("float64 PI = 3.14159").unwrap();
242+
assert_eq!(field.fieldname, "PI");
243+
assert_eq!(field.field_type, Type::new("float64").unwrap());
244+
assert!(!field.is_array);
245+
assert_eq!(field.array_size, 1);
246+
assert!(field.is_constant);
247+
assert_eq!(field.value, "3.14159");
248+
}
249+
250+
#[test]
251+
fn test_getters() {
252+
let field = Field::new_with_type(Type::new("int32").unwrap(), "test_field");
253+
254+
assert_eq!(field.type_(), &Type::new("int32").unwrap());
255+
assert_eq!(field.name(), "test_field");
256+
assert!(!field.is_array());
257+
assert!(!field.is_constant());
258+
assert_eq!(field.array_size(), 1);
259+
assert_eq!(field.value(), "");
260+
}
261+
262+
#[test]
263+
fn test_change_type() {
264+
let mut field = Field::new_with_type(Type::new("int32").unwrap(), "test_field");
265+
let new_type = Type::new("float64").unwrap();
266+
field.change_type(new_type.clone());
267+
268+
assert_eq!(field.type_(), &new_type);
269+
}
270+
}

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pub mod field;
2+
pub mod message;
3+
pub mod msgspec;
4+
pub mod r#type;
5+
6+
pub use field::*;
7+
pub use message::*;
8+
pub use msgspec::*;
9+
pub use r#type::*;

0 commit comments

Comments
 (0)