r2r/common/src/lib.rs

147 lines
4.8 KiB
Rust

use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::Read;
use std::path::PathBuf;
#[derive(Debug)]
pub struct RosMsg {
pub module: String, // e.g. std_msgs
pub prefix: String, // e.g. "msg" or "srv"
pub name: String, // e.g. "String"
}
// TODO: actions and srv are similiar
pub fn get_all_ros_msgs() -> Vec<String> {
let resource_index_subfolder = "share/ament_index/resource_index";
let resource_type = "rosidl_interfaces";
let ament_prefix_var_name = "AMENT_PREFIX_PATH";
let ament_prefix_var = env::var(ament_prefix_var_name).expect("Source your ROS!");
let mut msgs: Vec<String> = Vec::new();
for ament_prefix_path in ament_prefix_var.split(":") {
// println!("prefix: {}", ament_prefix_path);
let path = PathBuf::from(ament_prefix_path);
let path = path.join(resource_index_subfolder);
let path = path.join(resource_type);
if let Ok(paths) = fs::read_dir(path) {
for path in paths {
// println!("PATH Name: {}", path.unwrap().path().display());
let path = path.unwrap().path();
let path2 = path.clone();
let file_name = path2.file_name().unwrap();
// println!("Messages for: {:?}", file_name);
if let Ok(mut file) = File::open(path) {
let mut s = String::new();
file.read_to_string(&mut s).unwrap();
let lines = s.lines();
lines.for_each(|l| {
if l.starts_with("msg/") && (l.ends_with(".idl") || l.ends_with(".msg")) {
if let Some(file_name_str) = file_name.to_str() {
let substr = &l[4..l.len()-4];
let msg_name = format!("{}/msg/{}", file_name_str, substr);
msgs.push(msg_name);
}
}
if l.starts_with("srv/") && (l.ends_with(".idl") || l.ends_with(".srv")) {
if let Some(file_name_str) = file_name.to_str() {
let substr = &l[4..l.len()-4];
let srv_name = format!("{}/srv/{}", file_name_str, substr);
msgs.push(srv_name);
}
}
});
}
}
}
}
msgs.sort();
msgs.dedup();
return msgs;
}
#[test]
fn test_msg_list() {
let msgs = get_all_ros_msgs();
for m in &msgs {
println!("{}", m);
}
assert!(msgs.contains(&"std_msgs/msg/String".to_string()));
assert!(msgs.contains(&"builtin_interfaces/msg/Time".to_string()));
}
pub fn parse_msgs(msgs: &Vec<String>) -> Vec<RosMsg> {
let v: Vec<Vec<&str>> = msgs.iter().map(|l| l.split("/").into_iter().take(3).collect()).collect();
let v: Vec<_> = v.iter().filter(|v|v.len() == 3).
map(|v| RosMsg { module: v[0].into(), prefix: v[1].into(), name: v[2].into()}).collect();
// hack because I don't have time to find out the root cause of this at the moment.
// for some reason the library files generated to this are called
// liblibstatistics_collector_test_msgs__..., but I don't know where test_msgs come from.
// (this seems to be a useless package anyway)
// also affects message generation below.
v.into_iter().filter(|v| v.module != "libstatistics_collector").collect()
}
pub fn as_map(included_msgs: &[RosMsg]) -> HashMap<&str, HashMap<&str, Vec<&str>>> {
let mut msgs = HashMap::new();
for msg in included_msgs {
msgs.entry(msg.module.as_str()).or_insert(HashMap::new()).entry(msg.prefix.as_str()).
or_insert(Vec::new()).push(msg.name.as_str());
}
msgs
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_msgs() -> () {
let msgs = "
std_msgs/msg/Bool
x/y
std_msgs/msg/String
";
let msgs = msgs.lines().map(|l|l.to_string()).collect();
let parsed = parse_msgs(&msgs);
assert_eq!(parsed[0].module, "std_msgs");
assert_eq!(parsed[0].prefix, "msg");
assert_eq!(parsed[0].name, "Bool");
assert_eq!(parsed[1].module, "std_msgs");
assert_eq!(parsed[1].prefix, "msg");
assert_eq!(parsed[1].name, "String");
}
#[test]
fn test_as_map() -> () {
let msgs = "
std_msgs/msg/Bool
x/y
std_msgs/msg/String
";
let msgs: Vec<String> = msgs.lines().map(|l|l.to_string()).collect();
let parsed = parse_msgs(&msgs);
let map = as_map(&parsed);
assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[0], "Bool");
assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[1], "String");
}
}