apply rustfmt

This commit is contained in:
Martin Dahl 2021-05-15 11:04:32 +02:00
parent 3ae62f172a
commit 6b26328d65
17 changed files with 408 additions and 276 deletions

View File

@ -3,21 +3,29 @@ use msg_gen;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::{Path,PathBuf}; use std::path::{Path, PathBuf};
fn main() { fn main() {
common::print_cargo_watches(); common::print_cargo_watches();
let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() { let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() {
let packages = cmake_includes.split(":").flat_map(|i| Path::new(i).parent()).collect::<Vec<_>>(); let packages = cmake_includes
.split(":")
.flat_map(|i| Path::new(i).parent())
.collect::<Vec<_>>();
let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default()); let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default());
let deps = deps.split(":").collect::<Vec<_>>(); let deps = deps.split(":").collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&packages); let msgs = common::get_ros_msgs(&packages);
common::parse_msgs(&msgs).into_iter() common::parse_msgs(&msgs)
.filter(|msg| deps.contains(&msg.module.as_str())).collect::<Vec<_>>() .into_iter()
.filter(|msg| deps.contains(&msg.module.as_str()))
.collect::<Vec<_>>()
} else { } else {
let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!"); let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!");
let paths = ament_prefix_var.split(":").map(|i| Path::new(i)).collect::<Vec<_>>(); let paths = ament_prefix_var
.split(":")
.map(|i| Path::new(i))
.collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&paths); let msgs = common::get_ros_msgs(&paths);
common::parse_msgs(&msgs) common::parse_msgs(&msgs)
}; };
@ -26,7 +34,11 @@ fn main() {
let mut modules = String::new(); let mut modules = String::new();
for (module, prefixes) in &msgs { for (module, prefixes) in &msgs {
modules.push_str(&format!(r#"pub mod {module}{{include!(concat!(env!("OUT_DIR"), "/{module}.rs"));}}{lf}"#, module=module, lf="\n")); modules.push_str(&format!(
r#"pub mod {module}{{include!(concat!(env!("OUT_DIR"), "/{module}.rs"));}}{lf}"#,
module = module,
lf = "\n"
));
let mut codegen = String::new(); let mut codegen = String::new();
for (prefix, msgs) in prefixes { for (prefix, msgs) in prefixes {

View File

@ -14,7 +14,7 @@ pub fn print_cargo_watches() {
pub struct RosMsg { pub struct RosMsg {
pub module: String, // e.g. std_msgs pub module: String, // e.g. std_msgs
pub prefix: String, // e.g. "msg" or "srv" pub prefix: String, // e.g. "msg" or "srv"
pub name: String, // e.g. "String" pub name: String, // e.g. "String"
} }
fn get_msgs_from_package(package: &Path) -> Vec<String> { fn get_msgs_from_package(package: &Path) -> Vec<String> {
@ -28,7 +28,6 @@ fn get_msgs_from_package(package: &Path) -> Vec<String> {
let mut msgs = vec![]; let mut msgs = vec![];
if let Ok(paths) = fs::read_dir(path) { if let Ok(paths) = fs::read_dir(path) {
for path in paths { for path in paths {
// println!("PATH Name: {}", path.unwrap().path().display()); // println!("PATH Name: {}", path.unwrap().path().display());
@ -45,14 +44,14 @@ fn get_msgs_from_package(package: &Path) -> Vec<String> {
lines.for_each(|l| { lines.for_each(|l| {
if l.starts_with("msg/") && (l.ends_with(".idl") || l.ends_with(".msg")) { if l.starts_with("msg/") && (l.ends_with(".idl") || l.ends_with(".msg")) {
if let Some(file_name_str) = file_name.to_str() { if let Some(file_name_str) = file_name.to_str() {
let substr = &l[4..l.len()-4]; let substr = &l[4..l.len() - 4];
let msg_name = format!("{}/msg/{}", file_name_str, substr); let msg_name = format!("{}/msg/{}", file_name_str, substr);
msgs.push(msg_name); msgs.push(msg_name);
} }
} }
if l.starts_with("srv/") && (l.ends_with(".idl") || l.ends_with(".srv")) { if l.starts_with("srv/") && (l.ends_with(".idl") || l.ends_with(".srv")) {
if let Some(file_name_str) = file_name.to_str() { if let Some(file_name_str) = file_name.to_str() {
let substr = &l[4..l.len()-4]; let substr = &l[4..l.len() - 4];
let srv_name = format!("{}/srv/{}", file_name_str, substr); let srv_name = format!("{}/srv/{}", file_name_str, substr);
msgs.push(srv_name); msgs.push(srv_name);
} }
@ -81,23 +80,38 @@ pub fn get_ros_msgs(paths: &[&Path]) -> Vec<String> {
} }
pub fn parse_msgs(msgs: &Vec<String>) -> Vec<RosMsg> { 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<Vec<&str>> = msgs
let v: Vec<_> = v.iter().filter(|v|v.len() == 3). .iter()
map(|v| RosMsg { module: v[0].into(), prefix: v[1].into(), name: v[2].into()}).collect(); .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. // 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 // 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. // liblibstatistics_collector_test_msgs__..., but I don't know where test_msgs come from.
// (this seems to be a useless package anyway) // (this seems to be a useless package anyway)
// also affects message generation below. // also affects message generation below.
v.into_iter().filter(|v| v.module != "libstatistics_collector").collect() v.into_iter()
.filter(|v| v.module != "libstatistics_collector")
.collect()
} }
pub fn as_map(included_msgs: &[RosMsg]) -> HashMap<&str, HashMap<&str, Vec<&str>>> { pub fn as_map(included_msgs: &[RosMsg]) -> HashMap<&str, HashMap<&str, Vec<&str>>> {
let mut msgs = HashMap::new(); let mut msgs = HashMap::new();
for msg in included_msgs { for msg in included_msgs {
msgs.entry(msg.module.as_str()).or_insert(HashMap::new()).entry(msg.prefix.as_str()). msgs.entry(msg.module.as_str())
or_insert(Vec::new()).push(msg.name.as_str()); .or_insert(HashMap::new())
.entry(msg.prefix.as_str())
.or_insert(Vec::new())
.push(msg.name.as_str());
} }
msgs msgs
} }
@ -113,7 +127,7 @@ std_msgs/msg/Bool
x/y x/y
std_msgs/msg/String std_msgs/msg/String
"; ";
let msgs = msgs.lines().map(|l|l.to_string()).collect(); let msgs = msgs.lines().map(|l| l.to_string()).collect();
let parsed = parse_msgs(&msgs); let parsed = parse_msgs(&msgs);
assert_eq!(parsed[0].module, "std_msgs"); assert_eq!(parsed[0].module, "std_msgs");
assert_eq!(parsed[0].prefix, "msg"); assert_eq!(parsed[0].prefix, "msg");
@ -130,15 +144,14 @@ std_msgs/msg/Bool
x/y x/y
std_msgs/msg/String std_msgs/msg/String
"; ";
let msgs: Vec<String> = msgs.lines().map(|l|l.to_string()).collect(); let msgs: Vec<String> = msgs.lines().map(|l| l.to_string()).collect();
let parsed = parse_msgs(&msgs); let parsed = parse_msgs(&msgs);
let map = as_map(&parsed); 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()[0], "Bool");
assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[1], "String"); assert_eq!(
map.get("std_msgs").unwrap().get("msg").unwrap()[1],
"String"
);
} }
} }

View File

@ -17,18 +17,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut c = 0; let mut c = 0;
loop { loop {
let req = AddTwoInts::Request { a: 10*c, b: c }; let req = AddTwoInts::Request { a: 10 * c, b: c };
let cb_req = req.clone(); let cb_req = req.clone();
let cb = Box::new(move |r: AddTwoInts::Response| let cb = Box::new(move |r: AddTwoInts::Response| {
println!("{} + {} = {}", cb_req.a, cb_req.b, r.sum)); println!("{} + {} = {}", cb_req.a, cb_req.b, r.sum)
});
client.request(&req, cb)?; client.request(&req, cb)?;
node.spin_once(std::time::Duration::from_millis(1000)); node.spin_once(std::time::Duration::from_millis(1000));
std::thread::sleep(std::time::Duration::from_millis(1000)); std::thread::sleep(std::time::Duration::from_millis(1000));
c+=1; c += 1;
} }
} }

View File

@ -16,8 +16,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
0 => r2r::log_debug!(nl, "debug msg: {}", i as f64 / 2.5), 0 => r2r::log_debug!(nl, "debug msg: {}", i as f64 / 2.5),
1 => r2r::log_info!(nl, "info msg {}", i % 2), 1 => r2r::log_info!(nl, "info msg {}", i % 2),
2 => r2r::log_warn!(nl, "warn msg {:?}", i.to_string()), 2 => r2r::log_warn!(nl, "warn msg {:?}", i.to_string()),
3 => r2r::log_error!(nl, "error msg {:?}", 3 => r2r::log_error!(nl, "error msg {:?}", i.to_string().as_bytes()),
i.to_string().as_bytes()),
_ => r2r::log_fatal!(nl, "fatal msg {:#X}", i), _ => r2r::log_fatal!(nl, "fatal msg {:#X}", i),
} }
@ -25,6 +24,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// non-node logger only outputs to stdout // non-node logger only outputs to stdout
r2r::log_debug!("other_logger", "i = {}", i); r2r::log_debug!("other_logger", "i = {}", i);
i+=1; i += 1;
} }
} }

View File

@ -8,11 +8,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let node = r2r::Node::create(ctx, "testnode", "")?; let node = r2r::Node::create(ctx, "testnode", "")?;
println!("node name: {}", node.name()?); println!("node name: {}", node.name()?);
println!("node fully qualified name: {}", node.fully_qualified_name()?); println!(
"node fully qualified name: {}",
node.fully_qualified_name()?
);
println!("node namespace: {}", node.namespace()?); println!("node namespace: {}", node.namespace()?);
println!("node parameters"); println!("node parameters");
node.params.iter().for_each(|(k,v)| { node.params.iter().for_each(|(k, v)| {
println!("{} - {:?}", k, v); println!("{} - {:?}", k, v);
}); });

View File

@ -1,7 +1,7 @@
use r2r; use r2r;
use r2r::builtin_interfaces::msg::Duration; use r2r::builtin_interfaces::msg::Duration;
use r2r::trajectory_msgs::msg::*;
use r2r::std_msgs::msg::Int32; use r2r::std_msgs::msg::Int32;
use r2r::trajectory_msgs::msg::*;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
@ -11,13 +11,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut c = 0; let mut c = 0;
let mut positions: Vec<f64> = Vec::new(); let mut positions: Vec<f64> = Vec::new();
let cb = move |x:r2r::std_msgs::msg::String| { let cb = move |x: r2r::std_msgs::msg::String| {
println!("at count {} got: {}", c, x.data); println!("at count {} got: {}", c, x.data);
c = c + 1; c = c + 1;
positions.push(c as f64); positions.push(c as f64);
let to_send = JointTrajectoryPoint { let to_send = JointTrajectoryPoint {
positions: positions.clone(), positions: positions.clone(),
time_from_start : Duration { sec: c, nanosec: 0 }, time_from_start: Duration { sec: c, nanosec: 0 },
..Default::default() ..Default::default()
}; };
let mut native = r2r::WrappedNativeMsg::<Int32>::new(); let mut native = r2r::WrappedNativeMsg::<Int32>::new();
@ -27,12 +27,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
publisher2.publish_native(&native).unwrap(); publisher2.publish_native(&native).unwrap();
}; };
let cb2 = move |x:JointTrajectoryPoint| { let cb2 = move |x: JointTrajectoryPoint| {
let serialized = serde_json::to_string(&x).unwrap(); let serialized = serde_json::to_string(&x).unwrap();
println!("JTP serialized as: {}", serialized); println!("JTP serialized as: {}", serialized);
}; };
let cb3 = move |raw_c:&r2r::WrappedNativeMsg<JointTrajectoryPoint>| { let cb3 = move |raw_c: &r2r::WrappedNativeMsg<JointTrajectoryPoint>| {
println!("Raw c data: {:?}", raw_c.positions); println!("Raw c data: {:?}", raw_c.positions);
}; };

View File

@ -1,7 +1,7 @@
use r2r; use r2r;
use std::thread;
use std::env;
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::thread;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
@ -16,17 +16,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
while count < 50 { while count < 50 {
thread::sleep(std::time::Duration::from_millis(10)); thread::sleep(std::time::Duration::from_millis(10));
nt = node.get_topic_names_and_types()?; nt = node.get_topic_names_and_types()?;
if nt.contains_key(topic) { break; } if nt.contains_key(topic) {
break;
}
count += 1; count += 1;
} }
let type_name = nt.get(topic).and_then(|types|types.get(0)); let type_name = nt.get(topic).and_then(|types| types.get(0));
let type_name = match type_name { let type_name = match type_name {
Some(tn) => tn, Some(tn) => tn,
None => { None => {
eprintln!("Could not determine the type for the passed topic"); eprintln!("Could not determine the type for the passed topic");
return Ok(()); return Ok(());
}, }
}; };
println!("topic: {}, type: {}", topic, type_name); println!("topic: {}, type: {}", topic, type_name);
@ -35,16 +37,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let echo = &format!("{}_echo", topic); let echo = &format!("{}_echo", topic);
let echo_pub = node.create_publisher_untyped(echo, type_name)?; let echo_pub = node.create_publisher_untyped(echo, type_name)?;
let cb = move |msg: r2r::Result<serde_json::Value> | { let cb = move |msg: r2r::Result<serde_json::Value>| match msg {
match msg { Ok(msg) => {
Ok(msg) => { let s = serde_json::to_string_pretty(&msg).unwrap();
let s = serde_json::to_string_pretty(&msg).unwrap(); println!("{}\n---\n", &s);
println!("{}\n---\n", &s); echo_pub.publish(msg).unwrap();
echo_pub.publish(msg).unwrap(); }
} Err(err) => {
Err(err) => { println!("Could not parse msg. {}", err);
println!("Could not parse msg. {}", err);
}
} }
}; };

View File

@ -15,5 +15,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("{}: {:?}", k, nt[k]); println!("{}: {:?}", k, nt[k]);
} }
thread::sleep(Duration::from_millis(500)); thread::sleep(Duration::from_millis(500));
}; }
} }

View File

@ -4,7 +4,7 @@ use r2r::example_interfaces::srv::AddTwoInts;
fn handle_service(request: AddTwoInts::Request) -> AddTwoInts::Response { fn handle_service(request: AddTwoInts::Request) -> AddTwoInts::Response {
println!("request: {} + {}", request.a, request.b); println!("request: {} + {}", request.a, request.b);
AddTwoInts::Response { AddTwoInts::Response {
sum: request.a + request.b sum: request.a + request.b,
} }
} }

View File

@ -6,8 +6,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut x = 0; let mut x = 0;
let cb = move |elapsed: std::time::Duration| { let cb = move |elapsed: std::time::Duration| {
println!("timer called ({}), {}us since last call", x, elapsed.as_micros()); println!(
x+=1; "timer called ({}), {}us since last call",
x,
elapsed.as_micros()
);
x += 1;
}; };
node.create_wall_timer(std::time::Duration::from_millis(2000), Box::new(cb))?; node.create_wall_timer(std::time::Duration::from_millis(2000), Box::new(cb))?;

View File

@ -1,9 +1,9 @@
use bindgen;
use common;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::{Path,PathBuf}; use std::path::{Path, PathBuf};
use bindgen;
use common;
fn main() { fn main() {
common::print_cargo_watches(); common::print_cargo_watches();
@ -11,22 +11,30 @@ fn main() {
let mut builder = bindgen::Builder::default(); let mut builder = bindgen::Builder::default();
let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() { let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() {
let packages = cmake_includes.split(":").flat_map(|i| Path::new(i).parent()).collect::<Vec<_>>(); let packages = cmake_includes
.split(":")
.flat_map(|i| Path::new(i).parent())
.collect::<Vec<_>>();
for p in cmake_includes.split(":") { for p in cmake_includes.split(":") {
builder = builder.clang_arg(format!("-I{}", p)); builder = builder.clang_arg(format!("-I{}", p));
} }
let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default()); let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default());
let deps = deps.split(":").collect::<Vec<_>>(); let deps = deps.split(":").collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&packages); let msgs = common::get_ros_msgs(&packages);
common::parse_msgs(&msgs).into_iter() common::parse_msgs(&msgs)
.filter(|msg| deps.contains(&msg.module.as_str())).collect::<Vec<_>>() .into_iter()
.filter(|msg| deps.contains(&msg.module.as_str()))
.collect::<Vec<_>>()
} else { } else {
let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!"); let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!");
for p in ament_prefix_var.split(":") { for p in ament_prefix_var.split(":") {
builder = builder.clang_arg(format!("-I{}/include", p)); builder = builder.clang_arg(format!("-I{}/include", p));
} }
let paths = ament_prefix_var.split(":").map(|i| Path::new(i)).collect::<Vec<_>>(); let paths = ament_prefix_var
.split(":")
.map(|i| Path::new(i))
.collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&paths); let msgs = common::get_ros_msgs(&paths);
common::parse_msgs(&msgs) common::parse_msgs(&msgs)
}; };
@ -94,7 +102,7 @@ fn main() {
builder = builder builder = builder
.header(msg_includes_fn.to_str().unwrap()) .header(msg_includes_fn.to_str().unwrap())
.derive_copy(false) .derive_copy(false)
// blacklist types that are handled by rcl bindings // blacklist types that are handled by rcl bindings
.blacklist_type("rosidl_message_type_support_t") .blacklist_type("rosidl_message_type_support_t")
.blacklist_type("rosidl_service_type_support_t") .blacklist_type("rosidl_service_type_support_t")
.blacklist_type("rosidl_runtime_c__String") .blacklist_type("rosidl_runtime_c__String")

View File

@ -104,7 +104,7 @@ fn field_name(field_name: &str) -> String {
pub fn generate_rust_service(module_: &str, prefix_: &str, name_: &str) -> String { pub fn generate_rust_service(module_: &str, prefix_: &str, name_: &str) -> String {
format!( format!(
" "
pub struct Service(); pub struct Service();
impl WrappedServiceTypeSupport for Service {{ impl WrappedServiceTypeSupport for Service {{
type Request = Request; type Request = Request;
@ -116,10 +116,11 @@ pub fn generate_rust_service(module_: &str, prefix_: &str, name_: &str) -> Strin
}} }}
}} }}
", module_, prefix_, name_) ",
module_, prefix_, name_
)
} }
// TODO: this is a terrible hack :) // TODO: this is a terrible hack :)
pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String { pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
let key = format!("{}__{}__{}", module_, prefix_, name_); let key = format!("{}__{}__{}", module_, prefix_, name_);
@ -137,8 +138,12 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
// for srv, the message name is both the service name and _Request or _Respone // for srv, the message name is both the service name and _Request or _Respone
// we only want to keep the last part. // we only want to keep the last part.
let mut nn = name.splitn(2, "_"); let mut nn = name.splitn(2, "_");
let _mod_name = nn.next().expect(&format!("malformed service name {}", name)); let _mod_name = nn
let msg_name = nn.next().expect(&format!("malformed service name {}", name)); .next()
.expect(&format!("malformed service name {}", name));
let msg_name = nn
.next()
.expect(&format!("malformed service name {}", name));
name = msg_name.to_owned(); name = msg_name.to_owned();
} }
@ -262,14 +267,16 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
copy_to_native.push_str(&format!("assert_eq!(self.{field_name}.len(), {array_size}, \"Field {{}} is fixed size of {{}}!\", \"{field_name}\", {array_size});\n", field_name = field_name, array_size = member.array_size_)); copy_to_native.push_str(&format!("assert_eq!(self.{field_name}.len(), {array_size}, \"Field {{}} is fixed size of {{}}!\", \"{field_name}\", {array_size});\n", field_name = field_name, array_size = member.array_size_));
if rust_field_type == "message" { if rust_field_type == "message" {
copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ s.copy_to_native(t);}}\n", field_name=field_name)); copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ s.copy_to_native(t);}}\n", field_name=field_name));
} } else if rust_field_type == "std::string::String" {
else if rust_field_type == "std::string::String" {
copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ t.assign(&s);}}\n", field_name=field_name)); copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ t.assign(&s);}}\n", field_name=field_name));
} else { } else {
copy_to_native.push_str(&format!("msg.{field_name}.copy_from_slice(&self.{field_name}[..{array_size}]);\n", field_name = field_name, array_size = member.array_size_)); copy_to_native.push_str(&format!(
"msg.{field_name}.copy_from_slice(&self.{field_name}[..{array_size}]);\n",
field_name = field_name,
array_size = member.array_size_
));
} }
} } else if member.is_array_ && (member.array_size_ == 0 || member.is_upper_bound_) {
else if member.is_array_ && (member.array_size_ == 0 || member.is_upper_bound_) {
// these are __Sequence:s // these are __Sequence:s
if rust_field_type == "message" { if rust_field_type == "message" {
let (_, _, _, c_struct, _) = introspection(member.members_); let (_, _, _, c_struct, _) = introspection(member.members_);
@ -339,7 +346,9 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
{msgname}::from_native(&msg_native) {msgname}::from_native(&msg_native)
}} }}
}} }}
", msgname = name); ",
msgname = name
);
let module_str = format!( let module_str = format!(
" "
@ -381,7 +390,9 @@ impl WrappedNativeMsgUntyped {
let mut lines = String::new(); let mut lines = String::new();
for msg in msgs { for msg in msgs {
// for now don't generate untyped services // for now don't generate untyped services
if msg.prefix == "srv" { continue; } if msg.prefix == "srv" {
continue;
}
let typename = format!("{}/{}/{}", msg.module, msg.prefix, msg.name); let typename = format!("{}/{}/{}", msg.module, msg.prefix, msg.name);
let rustname = format!("{}::{}::{}", msg.module, msg.prefix, msg.name); let rustname = format!("{}::{}::{}", msg.module, msg.prefix, msg.name);

View File

@ -1,9 +1,9 @@
extern crate bindgen; extern crate bindgen;
use std::env;
use std::path::{Path,PathBuf};
use itertools::Itertools;
use common; use common;
use itertools::Itertools;
use std::env;
use std::path::{Path, PathBuf};
fn main() { fn main() {
common::print_cargo_watches(); common::print_cargo_watches();
@ -28,7 +28,9 @@ fn main() {
builder = builder.clang_arg(clang_arg); builder = builder.clang_arg(clang_arg);
} }
env::var("CMAKE_LIBRARIES").unwrap_or(String::new()).split(":") env::var("CMAKE_LIBRARIES")
.unwrap_or(String::new())
.split(":")
.into_iter() .into_iter()
.filter(|s| s.contains(".so") || s.contains(".dylib")) .filter(|s| s.contains(".so") || s.contains(".dylib"))
.flat_map(|l| Path::new(l).parent().and_then(|p| p.to_str())) .flat_map(|l| Path::new(l).parent().and_then(|p| p.to_str()))
@ -46,10 +48,12 @@ fn main() {
for ament_prefix_path in ament_prefix_var.split(":") { for ament_prefix_path in ament_prefix_var.split(":") {
builder = builder.clang_arg(format!("-I{}/include", ament_prefix_path)); builder = builder.clang_arg(format!("-I{}/include", ament_prefix_path));
println!("added include search dir: {}" , format!("-I{}/include", ament_prefix_path)); println!(
"added include search dir: {}",
format!("-I{}/include", ament_prefix_path)
);
println!("cargo:rustc-link-search=native={}/lib", ament_prefix_path); println!("cargo:rustc-link-search=native={}/lib", ament_prefix_path);
} }
} }
println!("cargo:rustc-link-lib=dylib=rcl"); println!("cargo:rustc-link-lib=dylib=rcl");
@ -62,7 +66,8 @@ fn main() {
let bindings = builder let bindings = builder
.no_debug("_OSUnaligned.*") .no_debug("_OSUnaligned.*")
.generate().expect("Unable to generate bindings"); .generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings bindings

View File

@ -17,24 +17,20 @@ impl Default for rmw_message_info_t {
impl Default for rmw_qos_profile_t { impl Default for rmw_qos_profile_t {
fn default() -> Self { fn default() -> Self {
let mut profile: rmw_qos_profile_t = unsafe { std::mem::zeroed() }; let mut profile: rmw_qos_profile_t = unsafe { std::mem::zeroed() };
profile.history = profile.history = rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;
rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;
profile.depth = 10; profile.depth = 10;
profile.reliability = profile.reliability =
rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT; rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
profile.durability = profile.durability = rmw_qos_durability_policy_t::RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT;
rmw_qos_durability_policy_t::RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT;
profile.avoid_ros_namespace_conventions = false; profile.avoid_ros_namespace_conventions = false;
profile.deadline = rmw_time_t { sec: 0, nsec: 0 }; profile.deadline = rmw_time_t { sec: 0, nsec: 0 };
profile.lifespan = rmw_time_t { sec: 0, nsec: 0 }; profile.lifespan = rmw_time_t { sec: 0, nsec: 0 };
profile.liveliness = profile.liveliness = rmw_qos_liveliness_policy_t::RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT;
rmw_qos_liveliness_policy_t::RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT;
profile.liveliness_lease_duration = rmw_time_t { sec: 0, nsec: 0 }; profile.liveliness_lease_duration = rmw_time_t { sec: 0, nsec: 0 };
profile profile
} }
} }
// special treatment to convert to/from rust strings. // special treatment to convert to/from rust strings.
// ros strings are owned by ros, assignment is a copy // ros strings are owned by ros, assignment is a copy
impl rosidl_runtime_c__String { impl rosidl_runtime_c__String {
@ -72,8 +68,12 @@ impl rosidl_runtime_c__U16String {
impl rosidl_runtime_c__U16String__Sequence { impl rosidl_runtime_c__U16String__Sequence {
pub fn update(&mut self, values: &[String]) { pub fn update(&mut self, values: &[String]) {
unsafe { rosidl_runtime_c__U16String__Sequence__fini(self as *mut _); } unsafe {
unsafe { rosidl_runtime_c__U16String__Sequence__init(self as *mut _, values.len()); } rosidl_runtime_c__U16String__Sequence__fini(self as *mut _);
}
unsafe {
rosidl_runtime_c__U16String__Sequence__init(self as *mut _, values.len());
}
let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) }; let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) };
for (target, source) in strs.iter_mut().zip(values) { for (target, source) in strs.iter_mut().zip(values) {
target.assign(&source); target.assign(&source);
@ -92,8 +92,12 @@ impl rosidl_runtime_c__U16String__Sequence {
impl rosidl_runtime_c__String__Sequence { impl rosidl_runtime_c__String__Sequence {
pub fn update(&mut self, values: &[String]) { pub fn update(&mut self, values: &[String]) {
unsafe { rosidl_runtime_c__String__Sequence__fini(self as *mut _); } unsafe {
unsafe { rosidl_runtime_c__String__Sequence__init(self as *mut _, values.len()); } rosidl_runtime_c__String__Sequence__fini(self as *mut _);
}
unsafe {
rosidl_runtime_c__String__Sequence__init(self as *mut _, values.len());
}
let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) }; let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) };
for (target, source) in strs.iter_mut().zip(values) { for (target, source) in strs.iter_mut().zip(values) {
target.assign(&source); target.assign(&source);
@ -130,7 +134,7 @@ macro_rules! primitive_sequence {
} }
} }
} }
} };
} }
primitive_sequence!(rosidl_runtime_c__float32, f32); primitive_sequence!(rosidl_runtime_c__float32, f32);

4
rustfmt.toml Normal file
View File

@ -0,0 +1,4 @@
hard_tabs = false
tab_spaces = 4
reorder_imports = true
newline_style = "Unix"

View File

@ -1,13 +1,16 @@
include!(concat!(env!("OUT_DIR"), "/_r2r_generated_msgs.rs")); include!(concat!(env!("OUT_DIR"), "/_r2r_generated_msgs.rs"));
include!(concat!(env!("OUT_DIR"), "/_r2r_generated_untyped_helper.rs")); include!(concat!(
env!("OUT_DIR"),
"/_r2r_generated_untyped_helper.rs"
));
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ffi::{CString,CStr}; use std::collections::HashMap;
use std::mem::MaybeUninit; use std::ffi::{CStr, CString};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::time::Duration; use std::time::Duration;
use std::collections::HashMap;
use msg_gen::*; use msg_gen::*;
use rcl::*; use rcl::*;
@ -37,7 +40,6 @@ pub trait WrappedServiceTypeSupport {
fn get_ts() -> &'static rosidl_service_type_support_t; fn get_ts() -> &'static rosidl_service_type_support_t;
} }
#[derive(Debug)] #[derive(Debug)]
pub struct WrappedNativeMsg<T> pub struct WrappedNativeMsg<T>
where where
@ -51,27 +53,33 @@ pub struct WrappedNativeMsgUntyped {
ts: &'static rosidl_message_type_support_t, ts: &'static rosidl_message_type_support_t,
msg: *mut std::os::raw::c_void, msg: *mut std::os::raw::c_void,
destroy: fn(*mut std::os::raw::c_void), destroy: fn(*mut std::os::raw::c_void),
msg_to_json: fn(native: *const std::os::raw::c_void) -> msg_to_json: fn(
std::result::Result<serde_json::Value, serde_json::error::Error>, native: *const std::os::raw::c_void,
msg_from_json: fn(native: *mut std::os::raw::c_void, json: serde_json::Value) -> ) -> std::result::Result<serde_json::Value, serde_json::error::Error>,
std::result::Result<(), serde_json::error::Error>, msg_from_json: fn(
native: *mut std::os::raw::c_void,
json: serde_json::Value,
) -> std::result::Result<(), serde_json::error::Error>,
} }
impl WrappedNativeMsgUntyped { impl WrappedNativeMsgUntyped {
fn new<T>() -> Self where T: WrappedTypesupport { fn new<T>() -> Self
let destroy = | native: *mut std::os::raw::c_void | { where
T: WrappedTypesupport,
{
let destroy = |native: *mut std::os::raw::c_void| {
let native_msg = native as *mut T::CStruct; let native_msg = native as *mut T::CStruct;
T::destroy_msg(native_msg); T::destroy_msg(native_msg);
}; };
let msg_to_json = | native: *const std::os::raw::c_void | { let msg_to_json = |native: *const std::os::raw::c_void| {
let msg = unsafe { T::from_native(&*(native as *const T::CStruct)) }; let msg = unsafe { T::from_native(&*(native as *const T::CStruct)) };
serde_json::to_value(&msg) serde_json::to_value(&msg)
}; };
let msg_from_json = | native: *mut std::os::raw::c_void, json: serde_json::Value | { let msg_from_json = |native: *mut std::os::raw::c_void, json: serde_json::Value| {
serde_json::from_value(json).map(|msg: T| { serde_json::from_value(json).map(|msg: T| unsafe {
unsafe { msg.copy_to_native(&mut *(native as *mut T::CStruct)); } msg.copy_to_native(&mut *(native as *mut T::CStruct));
}) })
}; };
@ -86,12 +94,15 @@ impl WrappedNativeMsgUntyped {
fn to_json(&self) -> Result<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
let json = (self.msg_to_json)(self.msg); let json = (self.msg_to_json)(self.msg);
json.map_err(|serde_err|Error::SerdeError { err: serde_err.to_string() }) json.map_err(|serde_err| Error::SerdeError {
err: serde_err.to_string(),
})
} }
fn from_json(&mut self, json: serde_json::Value) -> Result<()> { fn from_json(&mut self, json: serde_json::Value) -> Result<()> {
(self.msg_from_json)(self.msg, json). (self.msg_from_json)(self.msg, json).map_err(|serde_err| Error::SerdeError {
map_err(|serde_err|Error::SerdeError { err: serde_err.to_string() }) err: serde_err.to_string(),
})
} }
pub fn void_ptr(&self) -> *const std::os::raw::c_void { pub fn void_ptr(&self) -> *const std::os::raw::c_void {
@ -244,8 +255,7 @@ where
} }
} }
impl Sub for WrappedSubUntyped impl Sub for WrappedSubUntyped {
{
fn handle(&self) -> &rcl_subscription_t { fn handle(&self) -> &rcl_subscription_t {
&self.rcl_handle &self.rcl_handle
} }
@ -266,7 +276,6 @@ impl Sub for WrappedSubUntyped
} }
} }
// services // services
struct WrappedService<T> struct WrappedService<T>
where where
@ -308,7 +317,11 @@ where
let response = (self.callback)(request); let response = (self.callback)(request);
let mut native_response = WrappedNativeMsg::<T::Response>::from(&response); let mut native_response = WrappedNativeMsg::<T::Response>::from(&response);
let res = unsafe { let res = unsafe {
rcl_send_response(&self.rcl_handle, &mut self.rcl_request, native_response.void_ptr_mut()) rcl_send_response(
&self.rcl_handle,
&mut self.rcl_request,
native_response.void_ptr_mut(),
)
}; };
// TODO // TODO
@ -370,10 +383,16 @@ where
} else { } else {
// I don't think this should be able to occur? Let's panic so we // I don't think this should be able to occur? Let's panic so we
// find out... // find out...
let we_have: String = self.callbacks.iter() let we_have: String = self
.callbacks
.iter()
.map(|(id, _)| id.to_string()) .map(|(id, _)| id.to_string())
.collect::<Vec<_>>().join(","); .collect::<Vec<_>>()
eprintln!("no such req id: {}, we have [{}], ignoring", req_id, we_have); .join(",");
eprintln!(
"no such req id: {}, we have [{}], ignoring",
req_id, we_have
);
} }
} }
@ -438,7 +457,6 @@ where
client_: Weak<Mutex<WrappedClient<T>>>, client_: Weak<Mutex<WrappedClient<T>>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
context_handle: Arc<Mutex<ContextHandle>>, context_handle: Arc<Mutex<ContextHandle>>,
@ -454,23 +472,35 @@ impl Context {
pub fn create() -> Result<Context> { pub fn create() -> Result<Context> {
let mut ctx: Box<rcl_context_t> = unsafe { Box::new(rcl_get_zero_initialized_context()) }; let mut ctx: Box<rcl_context_t> = unsafe { Box::new(rcl_get_zero_initialized_context()) };
// argc/v // argc/v
let args = std::env::args().map(|arg| CString::new(arg).unwrap() ).collect::<Vec<CString>>(); let args = std::env::args()
let mut c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const ::std::os::raw::c_char>>(); .map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
let mut c_args = args
.iter()
.map(|arg| arg.as_ptr())
.collect::<Vec<*const ::std::os::raw::c_char>>();
c_args.push(std::ptr::null()); c_args.push(std::ptr::null());
let is_valid = unsafe { let is_valid = unsafe {
let allocator = rcutils_get_default_allocator(); let allocator = rcutils_get_default_allocator();
let mut init_options = rcl_get_zero_initialized_init_options(); let mut init_options = rcl_get_zero_initialized_init_options();
rcl_init_options_init(&mut init_options, allocator); rcl_init_options_init(&mut init_options, allocator);
rcl_init((c_args.len() - 1) as ::std::os::raw::c_int, c_args.as_ptr(), &init_options, ctx.as_mut()); rcl_init(
(c_args.len() - 1) as ::std::os::raw::c_int,
c_args.as_ptr(),
&init_options,
ctx.as_mut(),
);
rcl_init_options_fini(&mut init_options as *mut _); rcl_init_options_fini(&mut init_options as *mut _);
rcl_context_is_valid(ctx.as_mut()) rcl_context_is_valid(ctx.as_mut())
}; };
let logging_ok = unsafe { let logging_ok = unsafe {
let _guard = log_guard(); let _guard = log_guard();
let ret = rcl_logging_configure(&ctx.as_ref().global_arguments, let ret = rcl_logging_configure(
&rcutils_get_default_allocator()); &ctx.as_ref().global_arguments,
&rcutils_get_default_allocator(),
);
ret == RCL_RET_OK as i32 ret == RCL_RET_OK as i32
}; };
@ -544,50 +574,46 @@ impl ParameterValue {
ParameterValue::String(string) ParameterValue::String(string)
} else if v.byte_array_value != std::ptr::null_mut() { } else if v.byte_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts((*v.byte_array_value).values, (*v.byte_array_value).size)
(*v.byte_array_value).values,
(*v.byte_array_value).size)
}; };
ParameterValue::ByteArray(vals.iter().cloned().collect()) ParameterValue::ByteArray(vals.iter().cloned().collect())
} } else if v.bool_array_value != std::ptr::null_mut() {
else if v.bool_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts((*v.bool_array_value).values, (*v.bool_array_value).size)
(*v.bool_array_value).values,
(*v.bool_array_value).size)
}; };
ParameterValue::BoolArray(vals.iter().cloned().collect()) ParameterValue::BoolArray(vals.iter().cloned().collect())
} } else if v.integer_array_value != std::ptr::null_mut() {
else if v.integer_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*v.integer_array_value).values, (*v.integer_array_value).values,
(*v.integer_array_value).size) (*v.integer_array_value).size,
)
}; };
ParameterValue::IntegerArray(vals.iter().cloned().collect()) ParameterValue::IntegerArray(vals.iter().cloned().collect())
} } else if v.double_array_value != std::ptr::null_mut() {
else if v.double_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*v.double_array_value).values, (*v.double_array_value).values,
(*v.double_array_value).size) (*v.double_array_value).size,
)
}; };
ParameterValue::DoubleArray(vals.iter().cloned().collect()) ParameterValue::DoubleArray(vals.iter().cloned().collect())
} } else if v.string_array_value != std::ptr::null_mut() {
else if v.string_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*v.string_array_value).data, (*v.string_array_value).data,
(*v.string_array_value).size) (*v.string_array_value).size,
)
}; };
let s = vals.iter().map(|cs| { let s = vals
let s = unsafe { CStr::from_ptr(*cs) }; .iter()
s.to_str().unwrap_or("").to_owned() .map(|cs| {
}).collect(); let s = unsafe { CStr::from_ptr(*cs) };
s.to_str().unwrap_or("").to_owned()
})
.collect();
ParameterValue::StringArray(s) ParameterValue::StringArray(s)
} } else {
else {
ParameterValue::NotSet ParameterValue::NotSet
} }
} }
@ -641,9 +667,8 @@ impl Node {
let ctx = self.context.context_handle.lock().unwrap(); let ctx = self.context.context_handle.lock().unwrap();
let mut params: Box<*mut rcl_params_t> = Box::new(std::ptr::null_mut()); let mut params: Box<*mut rcl_params_t> = Box::new(std::ptr::null_mut());
let ret = unsafe { let ret =
rcl_arguments_get_param_overrides(&ctx.global_arguments, params.as_mut()) unsafe { rcl_arguments_get_param_overrides(&ctx.global_arguments, params.as_mut()) };
};
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not read parameters: {}", ret); eprintln!("could not read parameters: {}", ret);
return Err(Error::from_rcl_error(ret)); return Err(Error::from_rcl_error(ret));
@ -655,12 +680,16 @@ impl Node {
let node_names = unsafe { let node_names = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*(*params.as_ref())).node_names, (*(*params.as_ref())).num_nodes) (*(*params.as_ref())).node_names,
(*(*params.as_ref())).num_nodes,
)
}; };
let node_params = unsafe { let node_params = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*(*params.as_ref())).params, (*(*params.as_ref())).num_nodes) (*(*params.as_ref())).params,
(*(*params.as_ref())).num_nodes,
)
}; };
let qualified_name = self.fully_qualified_name()?; let qualified_name = self.fully_qualified_name()?;
@ -673,25 +702,22 @@ impl Node {
// This is copied from rclcpp, but there is a comment there suggesting // This is copied from rclcpp, but there is a comment there suggesting
// that more wildcards will be added in the future. Take note and mimic // that more wildcards will be added in the future. Take note and mimic
// their behavior. // their behavior.
if !( if !(node_name == "/**"
node_name == "/**" || || node_name == "**"
node_name == "**" || || qualified_name == node_name
qualified_name == node_name || || name == node_name)
name == node_name {
) {
continue; continue;
} }
// make key value pairs. // make key value pairs.
let param_names = unsafe { let param_names =
std::slice::from_raw_parts(np.parameter_names,np.num_params) unsafe { std::slice::from_raw_parts(np.parameter_names, np.num_params) };
};
let param_values = unsafe { let param_values =
std::slice::from_raw_parts(np.parameter_values,np.num_params) unsafe { std::slice::from_raw_parts(np.parameter_values, np.num_params) };
};
for (s,v) in param_names.iter().zip(param_values) { for (s, v) in param_names.iter().zip(param_values) {
let s = unsafe { CStr::from_ptr(*s) }; let s = unsafe { CStr::from_ptr(*s) };
let key = s.to_str().unwrap_or(""); let key = s.to_str().unwrap_or("");
let val = ParameterValue::from_rcl(&*v); let val = ParameterValue::from_rcl(&*v);
@ -699,11 +725,10 @@ impl Node {
} }
} }
unsafe { rcl_yaml_node_struct_fini(*params) } ; unsafe { rcl_yaml_node_struct_fini(*params) };
Ok(()) Ok(())
} }
pub fn create(ctx: Context, name: &str, namespace: &str) -> Result<Node> { pub fn create(ctx: Context, name: &str, namespace: &str) -> Result<Node> {
// cleanup default options. // cleanup default options.
let (res, node_handle) = { let (res, node_handle) = {
@ -745,9 +770,13 @@ impl Node {
} }
} }
fn create_subscription_helper(&mut self, topic: &str, ts: *const rosidl_message_type_support_t) -> Result<rcl_subscription_t> { fn create_subscription_helper(
&mut self,
topic: &str,
ts: *const rosidl_message_type_support_t,
) -> Result<rcl_subscription_t> {
let mut subscription_handle = unsafe { rcl_get_zero_initialized_subscription() }; let mut subscription_handle = unsafe { rcl_get_zero_initialized_subscription() };
let topic_c_string = CString::new(topic).map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?; let topic_c_string = CString::new(topic).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let mut subscription_options = rcl_subscription_get_default_options(); let mut subscription_options = rcl_subscription_get_default_options();
@ -822,17 +851,24 @@ impl Node {
Ok(self.subs.last().unwrap().handle()) // hmm... Ok(self.subs.last().unwrap().handle()) // hmm...
} }
pub fn create_service_helper(&mut self, service_name: &str, pub fn create_service_helper(
service_ts: *const rosidl_service_type_support_t) &mut self,
-> Result<rcl_service_t> { service_name: &str,
service_ts: *const rosidl_service_type_support_t,
) -> Result<rcl_service_t> {
let mut service_handle = unsafe { rcl_get_zero_initialized_service() }; let mut service_handle = unsafe { rcl_get_zero_initialized_service() };
let service_name_c_string = CString::new(service_name) let service_name_c_string =
.map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?; CString::new(service_name).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let service_options = rcl_service_get_default_options(); let service_options = rcl_service_get_default_options();
rcl_service_init(&mut service_handle, self.node_handle.as_mut(), rcl_service_init(
service_ts, service_name_c_string.as_ptr(), &service_options) &mut service_handle,
self.node_handle.as_mut(),
service_ts,
service_name_c_string.as_ptr(),
&service_options,
)
}; };
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
Ok(service_handle) Ok(service_handle)
@ -864,18 +900,24 @@ impl Node {
Ok(self.services.last().unwrap().handle()) // hmm... Ok(self.services.last().unwrap().handle()) // hmm...
} }
pub fn create_client_helper(
pub fn create_client_helper(&mut self, service_name: &str, &mut self,
service_ts: *const rosidl_service_type_support_t) service_name: &str,
-> Result<rcl_client_t> { service_ts: *const rosidl_service_type_support_t,
) -> Result<rcl_client_t> {
let mut client_handle = unsafe { rcl_get_zero_initialized_client() }; let mut client_handle = unsafe { rcl_get_zero_initialized_client() };
let service_name_c_string = CString::new(service_name) let service_name_c_string =
.map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?; CString::new(service_name).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let client_options = rcl_client_get_default_options(); let client_options = rcl_client_get_default_options();
rcl_client_init(&mut client_handle, self.node_handle.as_mut(), rcl_client_init(
service_ts, service_name_c_string.as_ptr(), &client_options) &mut client_handle,
self.node_handle.as_mut(),
service_ts,
service_name_c_string.as_ptr(),
&client_options,
)
}; };
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
Ok(client_handle) Ok(client_handle)
@ -884,16 +926,13 @@ impl Node {
} }
} }
pub fn create_client<T: 'static>( pub fn create_client<T: 'static>(&mut self, service_name: &str) -> Result<Client<T>>
&mut self,
service_name: &str,
) -> Result<Client<T>>
where where
T: WrappedServiceTypeSupport, T: WrappedServiceTypeSupport,
{ {
let client_handle = self.create_client_helper(service_name, T::get_ts())?; let client_handle = self.create_client_helper(service_name, T::get_ts())?;
let cloned_ch = rcl_client_t { let cloned_ch = rcl_client_t {
impl_: client_handle.impl_ impl_: client_handle.impl_,
}; };
let ws = WrappedClient::<T> { let ws = WrappedClient::<T> {
rcl_handle: cloned_ch, rcl_handle: cloned_ch,
@ -908,21 +947,22 @@ impl Node {
let arc = Arc::new(Mutex::new(ws)); let arc = Arc::new(Mutex::new(ws));
let client_ = Arc::downgrade(&arc); let client_ = Arc::downgrade(&arc);
self.clients.push((client_handle, arc)); self.clients.push((client_handle, arc));
let c = Client { let c = Client { client_ };
client_
};
Ok(c) Ok(c)
} }
pub fn service_available<T: WrappedServiceTypeSupport>(&self, client: &Client<T>) -> Result<bool> { pub fn service_available<T: WrappedServiceTypeSupport>(
let client = client.client_.upgrade().ok_or(Error::RCL_RET_CLIENT_INVALID)?; &self,
client: &Client<T>,
) -> Result<bool> {
let client = client
.client_
.upgrade()
.ok_or(Error::RCL_RET_CLIENT_INVALID)?;
let client = client.lock().unwrap(); let client = client.lock().unwrap();
let mut avail = false; let mut avail = false;
let result = unsafe { let result = unsafe {
rcl_service_server_is_available( rcl_service_server_is_available(self.node_handle.as_ref(), client.handle(), &mut avail)
self.node_handle.as_ref(),
client.handle(),
&mut avail)
}; };
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
@ -933,11 +973,13 @@ impl Node {
} }
} }
pub fn create_publisher_helper(&mut self, topic: &str, pub fn create_publisher_helper(
typesupport: *const rosidl_message_type_support_t) -> Result<rcl_publisher_t> { &mut self,
topic: &str,
typesupport: *const rosidl_message_type_support_t,
) -> Result<rcl_publisher_t> {
let mut publisher_handle = unsafe { rcl_get_zero_initialized_publisher() }; let mut publisher_handle = unsafe { rcl_get_zero_initialized_publisher() };
let topic_c_string = CString::new(topic) let topic_c_string = CString::new(topic).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
.map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let mut publisher_options = rcl_publisher_get_default_options(); let mut publisher_options = rcl_publisher_get_default_options();
@ -957,7 +999,6 @@ impl Node {
} }
} }
pub fn create_publisher<T>(&mut self, topic: &str) -> Result<Publisher<T>> pub fn create_publisher<T>(&mut self, topic: &str) -> Result<Publisher<T>>
where where
T: WrappedTypesupport, T: WrappedTypesupport,
@ -971,7 +1012,11 @@ impl Node {
Ok(p) Ok(p)
} }
pub fn create_publisher_untyped(&mut self, topic: &str, topic_type: &str) -> Result<PublisherUntyped> { pub fn create_publisher_untyped(
&mut self,
topic: &str,
topic_type: &str,
) -> Result<PublisherUntyped> {
let dummy = WrappedNativeMsgUntyped::new_from(topic_type)?; // TODO, get ts without allocating msg let dummy = WrappedNativeMsgUntyped::new_from(topic_type)?; // TODO, get ts without allocating msg
let publisher_handle = self.create_publisher_helper(topic, dummy.ts)?; let publisher_handle = self.create_publisher_helper(topic, dummy.ts)?;
@ -1038,9 +1083,7 @@ impl Node {
} }
} }
let ret = unsafe { let ret = unsafe { rcl_wait(&mut ws, timeout) };
rcl_wait(&mut ws, timeout)
};
if ret == RCL_RET_TIMEOUT as i32 { if ret == RCL_RET_TIMEOUT as i32 {
unsafe { unsafe {
@ -1064,21 +1107,19 @@ impl Node {
} }
} }
let ws_timers = let ws_timers = unsafe { std::slice::from_raw_parts(ws.timers, ws.size_of_timers) };
unsafe { std::slice::from_raw_parts(ws.timers, ws.size_of_timers) };
assert_eq!(ws_timers.len(), self.timers.len()); assert_eq!(ws_timers.len(), self.timers.len());
for (s, ws_s) in self.timers.iter_mut().zip(ws_timers) { for (s, ws_s) in self.timers.iter_mut().zip(ws_timers) {
if ws_s != &std::ptr::null() { if ws_s != &std::ptr::null() {
let mut is_ready = false; let mut is_ready = false;
let ret = unsafe { let ret = unsafe { rcl_timer_is_ready(&s.timer_handle, &mut is_ready) };
rcl_timer_is_ready(&s.timer_handle, &mut is_ready)
};
if ret == RCL_RET_OK as i32 { if ret == RCL_RET_OK as i32 {
if is_ready { if is_ready {
let mut nanos = 0i64; let mut nanos = 0i64;
// todo: error handling // todo: error handling
let ret = unsafe { rcl_timer_get_time_since_last_call(&s.timer_handle, let ret = unsafe {
&mut nanos) }; rcl_timer_get_time_since_last_call(&s.timer_handle, &mut nanos)
};
if ret == RCL_RET_OK as i32 { if ret == RCL_RET_OK as i32 {
let ret = unsafe { rcl_timer_call(&mut s.timer_handle) }; let ret = unsafe { rcl_timer_call(&mut s.timer_handle) };
if ret == RCL_RET_OK as i32 { if ret == RCL_RET_OK as i32 {
@ -1090,8 +1131,7 @@ impl Node {
} }
} }
let ws_clients = let ws_clients = unsafe { std::slice::from_raw_parts(ws.clients, ws.size_of_clients) };
unsafe { std::slice::from_raw_parts(ws.clients, ws.size_of_clients) };
assert_eq!(ws_clients.len(), self.clients.len()); assert_eq!(ws_clients.len(), self.clients.len());
for ((_, s), ws_s) in self.clients.iter_mut().zip(ws_clients) { for ((_, s), ws_s) in self.clients.iter_mut().zip(ws_clients) {
if ws_s != &std::ptr::null() { if ws_s != &std::ptr::null() {
@ -1105,8 +1145,7 @@ impl Node {
} }
} }
let ws_services = let ws_services = unsafe { std::slice::from_raw_parts(ws.services, ws.size_of_services) };
unsafe { std::slice::from_raw_parts(ws.services, ws.size_of_services) };
assert_eq!(ws_services.len(), self.services.len()); assert_eq!(ws_services.len(), self.services.len());
for (s, ws_s) in self.services.iter_mut().zip(ws_services) { for (s, ws_s) in self.services.iter_mut().zip(ws_services) {
if ws_s != &std::ptr::null() { if ws_s != &std::ptr::null() {
@ -1124,11 +1163,15 @@ impl Node {
} }
} }
pub fn get_topic_names_and_types(&self) -> Result<HashMap<String, Vec<String>>> { pub fn get_topic_names_and_types(&self) -> Result<HashMap<String, Vec<String>>> {
let mut tnat = unsafe { rmw_get_zero_initialized_names_and_types() }; let mut tnat = unsafe { rmw_get_zero_initialized_names_and_types() };
let ret = unsafe { let ret = unsafe {
rcl_get_topic_names_and_types(self.node_handle.as_ref(), &mut rcutils_get_default_allocator(), false, &mut tnat) rcl_get_topic_names_and_types(
self.node_handle.as_ref(),
&mut rcutils_get_default_allocator(),
false,
&mut tnat,
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not get topic names and types {}", ret); eprintln!("could not get topic names and types {}", ret);
@ -1139,27 +1182,35 @@ impl Node {
let types = unsafe { std::slice::from_raw_parts(tnat.types, tnat.names.size) }; let types = unsafe { std::slice::from_raw_parts(tnat.types, tnat.names.size) };
let mut res = HashMap::new(); let mut res = HashMap::new();
for (n,t) in names.iter().zip(types) { for (n, t) in names.iter().zip(types) {
let topic_name = unsafe {CStr::from_ptr(*n).to_str().unwrap().to_owned() }; let topic_name = unsafe { CStr::from_ptr(*n).to_str().unwrap().to_owned() };
let topic_types = unsafe { std::slice::from_raw_parts(t, t.size) }; let topic_types = unsafe { std::slice::from_raw_parts(t, t.size) };
let topic_types: Vec<String> = unsafe { let topic_types: Vec<String> = unsafe {
topic_types.iter().map(|t| CStr::from_ptr(*((*t).data)).to_str().unwrap().to_owned()).collect() topic_types
.iter()
.map(|t| CStr::from_ptr(*((*t).data)).to_str().unwrap().to_owned())
.collect()
}; };
res.insert(topic_name, topic_types); res.insert(topic_name, topic_types);
} }
unsafe { rmw_names_and_types_fini(&mut tnat); } // TODO: check return value unsafe {
rmw_names_and_types_fini(&mut tnat);
} // TODO: check return value
Ok(res) Ok(res)
} }
pub fn create_wall_timer( pub fn create_wall_timer(
&mut self, &mut self,
period: Duration, period: Duration,
callback: Box<dyn FnMut(Duration) -> ()>) -> Result<&Timer> { callback: Box<dyn FnMut(Duration) -> ()>,
) -> Result<&Timer> {
let mut clock_handle = MaybeUninit::<rcl_clock_t>::uninit(); let mut clock_handle = MaybeUninit::<rcl_clock_t>::uninit();
let ret = unsafe { let ret = unsafe {
rcl_steady_clock_init(clock_handle.as_mut_ptr(), &mut rcutils_get_default_allocator()) rcl_steady_clock_init(
clock_handle.as_mut_ptr(),
&mut rcutils_get_default_allocator(),
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not create steady clock: {}", ret); eprintln!("could not create steady clock: {}", ret);
@ -1172,9 +1223,14 @@ impl Node {
{ {
let mut ctx = self.context.context_handle.lock().unwrap(); let mut ctx = self.context.context_handle.lock().unwrap();
let ret = unsafe { let ret = unsafe {
rcl_timer_init(&mut timer_handle, clock_handle.as_mut(), rcl_timer_init(
ctx.as_mut(), period.as_nanos() as i64, &mut timer_handle,
None, rcutils_get_default_allocator()) clock_handle.as_mut(),
ctx.as_mut(),
period.as_nanos() as i64,
None,
rcutils_get_default_allocator(),
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
@ -1183,10 +1239,14 @@ impl Node {
} }
} }
let timer = Timer { timer_handle, clock_handle, callback }; let timer = Timer {
timer_handle,
clock_handle,
callback,
};
self.timers.push(timer); self.timers.push(timer);
Ok(&self.timers[self.timers.len()-1]) Ok(&self.timers[self.timers.len() - 1])
} }
pub fn logger<'a>(&'a self) -> &'a str { pub fn logger<'a>(&'a self) -> &'a str {
@ -1197,7 +1257,6 @@ impl Node {
let s = unsafe { CStr::from_ptr(ptr) }; let s = unsafe { CStr::from_ptr(ptr) };
s.to_str().unwrap_or("") s.to_str().unwrap_or("")
} }
} }
pub struct Timer { pub struct Timer {
@ -1260,7 +1319,10 @@ where
T: WrappedTypesupport, T: WrappedTypesupport,
{ {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let publisher = self.handle.upgrade().ok_or(Error::RCL_RET_PUBLISHER_INVALID)?; let publisher = self
.handle
.upgrade()
.ok_or(Error::RCL_RET_PUBLISHER_INVALID)?;
// copy rust msg to native and publish it // copy rust msg to native and publish it
let native_msg: WrappedNativeMsg<T> = WrappedNativeMsg::<T>::from(msg); let native_msg: WrappedNativeMsg<T> = WrappedNativeMsg::<T>::from(msg);
let result = unsafe { let result = unsafe {
@ -1284,7 +1346,10 @@ where
T: WrappedTypesupport, T: WrappedTypesupport,
{ {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let publisher = self.handle.upgrade().ok_or(Error::RCL_RET_PUBLISHER_INVALID)?; let publisher = self
.handle
.upgrade()
.ok_or(Error::RCL_RET_PUBLISHER_INVALID)?;
let result = let result =
unsafe { rcl_publish(publisher.as_ref(), msg.void_ptr(), std::ptr::null_mut()) }; unsafe { rcl_publish(publisher.as_ref(), msg.void_ptr(), std::ptr::null_mut()) };
@ -1297,7 +1362,6 @@ where
} }
} }
impl<T> Client<T> impl<T> Client<T>
where where
T: WrappedServiceTypeSupport, T: WrappedServiceTypeSupport,
@ -1307,18 +1371,16 @@ where
T: WrappedServiceTypeSupport, T: WrappedServiceTypeSupport,
{ {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let client = self.client_.upgrade().ok_or(Error::RCL_RET_CLIENT_INVALID)?; let client = self
.client_
.upgrade()
.ok_or(Error::RCL_RET_CLIENT_INVALID)?;
let mut client = client.lock().unwrap(); let mut client = client.lock().unwrap();
// copy rust msg to native and publish it // copy rust msg to native and publish it
let native_msg: WrappedNativeMsg<T::Request> = WrappedNativeMsg::<T::Request>::from(msg); let native_msg: WrappedNativeMsg<T::Request> = WrappedNativeMsg::<T::Request>::from(msg);
let mut seq_no = 0i64; let mut seq_no = 0i64;
let result = unsafe { let result =
rcl_send_request( unsafe { rcl_send_request(&client.rcl_handle, native_msg.void_ptr(), &mut seq_no) };
&client.rcl_handle,
native_msg.void_ptr(),
&mut seq_no,
)
};
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
client.callbacks.push((seq_no, cb)); client.callbacks.push((seq_no, cb));
@ -1330,11 +1392,13 @@ where
} }
} }
impl PublisherUntyped { impl PublisherUntyped {
pub fn publish(&self, msg: serde_json::Value) -> Result<()> { pub fn publish(&self, msg: serde_json::Value) -> Result<()> {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let publisher = self.handle.upgrade().ok_or(Error::RCL_RET_PUBLISHER_INVALID)?; let publisher = self
.handle
.upgrade()
.ok_or(Error::RCL_RET_PUBLISHER_INVALID)?;
let mut native_msg = WrappedNativeMsgUntyped::new_from(&self.type_)?; let mut native_msg = WrappedNativeMsgUntyped::new_from(&self.type_)?;
native_msg.from_json(msg)?; native_msg.from_json(msg)?;
@ -1383,7 +1447,11 @@ impl Clock {
let rcl_ct = Clock::clock_type_to_rcl(&ct); let rcl_ct = Clock::clock_type_to_rcl(&ct);
let ret = unsafe { let ret = unsafe {
rcl_clock_init(rcl_ct, clock_handle.as_mut_ptr(), &mut rcutils_get_default_allocator()) rcl_clock_init(
rcl_ct,
clock_handle.as_mut_ptr(),
&mut rcutils_get_default_allocator(),
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not create {:?} clock: {}", ct, ret); eprintln!("could not create {:?} clock: {}", ct, ret);
@ -1400,10 +1468,7 @@ impl Clock {
return Err(Error::from_rcl_error(RCL_RET_INVALID_ARGUMENT as i32)); return Err(Error::from_rcl_error(RCL_RET_INVALID_ARGUMENT as i32));
} }
let mut tp: rcutils_time_point_value_t = 0; let mut tp: rcutils_time_point_value_t = 0;
let ret = unsafe { let ret = unsafe { rcl_clock_get_now(&mut *self.clock_handle, &mut tp) };
rcl_clock_get_now(&mut *self.clock_handle,
&mut tp)
};
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not create steady clock: {}", ret); eprintln!("could not create steady clock: {}", ret);
@ -1418,10 +1483,7 @@ impl Clock {
pub fn to_builtin_time(d: &Duration) -> builtin_interfaces::msg::Time { pub fn to_builtin_time(d: &Duration) -> builtin_interfaces::msg::Time {
let sec = d.as_secs() as i32; let sec = d.as_secs() as i32;
let nanosec = d.subsec_nanos(); let nanosec = d.subsec_nanos();
builtin_interfaces::msg::Time { builtin_interfaces::msg::Time { sec, nanosec }
sec,
nanosec,
}
} }
} }
@ -1602,12 +1664,14 @@ mod tests {
msg.positions.push(34.0); msg.positions.push(34.0);
let json = serde_json::to_value(msg.clone()).unwrap(); let json = serde_json::to_value(msg.clone()).unwrap();
let mut native = WrappedNativeMsgUntyped::new_from("trajectory_msgs/msg/JointTrajectoryPoint").unwrap(); let mut native =
WrappedNativeMsgUntyped::new_from("trajectory_msgs/msg/JointTrajectoryPoint").unwrap();
native.from_json(json.clone()).unwrap(); native.from_json(json.clone()).unwrap();
let json2 = native.to_json().unwrap(); let json2 = native.to_json().unwrap();
assert_eq!(json, json2); assert_eq!(json, json2);
let msg2: trajectory_msgs::msg::JointTrajectoryPoint = serde_json::from_value(json2).unwrap(); let msg2: trajectory_msgs::msg::JointTrajectoryPoint =
serde_json::from_value(json2).unwrap();
assert_eq!(msg, msg2); assert_eq!(msg, msg2);
} }
@ -1616,9 +1680,7 @@ mod tests {
fn test_test_msgs_array() -> () { fn test_test_msgs_array() -> () {
let mut msg = test_msgs::msg::Arrays::default(); let mut msg = test_msgs::msg::Arrays::default();
println!("msg: {:?}", msg.string_values); println!("msg: {:?}", msg.string_values);
msg.string_values = vec![ msg.string_values = vec!["hej".to_string(), "hopp".to_string(), "stropp".to_string()];
"hej".to_string(), "hopp".to_string(), "stropp".to_string()
];
let msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg); let msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg);
let msg2 = test_msgs::msg::Arrays::from_native(&msg_native); let msg2 = test_msgs::msg::Arrays::from_native(&msg_native);
@ -1632,7 +1694,7 @@ mod tests {
fn test_test_msgs_array_too_few_elems() -> () { fn test_test_msgs_array_too_few_elems() -> () {
let mut msg = test_msgs::msg::Arrays::default(); let mut msg = test_msgs::msg::Arrays::default();
println!("msg: {:?}", msg.string_values); println!("msg: {:?}", msg.string_values);
msg.string_values = vec![ "hej".to_string(), "hopp".to_string() ]; msg.string_values = vec!["hej".to_string(), "hopp".to_string()];
let _msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg); let _msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg);
} }

View File

@ -1,6 +1,6 @@
use std::ffi::{CString};
use std::sync::{Mutex,MutexGuard};
use rcl::*; use rcl::*;
use std::ffi::CString;
use std::sync::{Mutex, MutexGuard};
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -37,11 +37,13 @@ pub fn log(msg: &str, logger_name: &str, file: &str, line: u32, severity: LogSev
let message = CString::new(msg).unwrap(); let message = CString::new(msg).unwrap();
let severity = severity.to_native(); let severity = severity.to_native();
unsafe { unsafe {
rcutils_log(&location, rcutils_log(
severity as i32, &location,
logger_name.as_ptr(), severity as i32,
format.as_ptr(), logger_name.as_ptr(),
message.as_ptr()); format.as_ptr(),
message.as_ptr(),
);
} }
} }
@ -52,7 +54,7 @@ pub enum LogSeverity {
Info, Info,
Warn, Warn,
Error, Error,
Fatal Fatal,
} }
impl LogSeverity { impl LogSeverity {
@ -74,8 +76,14 @@ impl LogSeverity {
#[macro_export] #[macro_export]
macro_rules! __impl_log { macro_rules! __impl_log {
($logger_name:expr, $msg:expr, $file:expr, $line:expr, $severity:expr) => {{ ($logger_name:expr, $msg:expr, $file:expr, $line:expr, $severity:expr) => {{
$crate::log(&std::fmt::format($msg), $logger_name, $file, $line, $severity); $crate::log(
}} &std::fmt::format($msg),
$logger_name,
$file,
$line,
$severity,
);
}};
} }
#[macro_export] #[macro_export]
@ -118,7 +126,6 @@ macro_rules! log_fatal {
}} }}
} }
#[test] #[test]
fn test_log() { fn test_log() {
log_debug!("log_test", "debug msg"); log_debug!("log_test", "debug msg");