Refactor codegen using syn and quote
This commit is contained in:
parent
c2c365fa77
commit
a042dc8c55
|
|
@ -25,6 +25,7 @@ r2r_actions = { path = "../r2r_actions", version = "0.3.10" }
|
|||
uuid = { version = "1.2.2", features = ["serde", "v4"] }
|
||||
futures = "0.3.25"
|
||||
log = "0.4.18"
|
||||
phf = "0.11.1"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0.89"
|
||||
|
|
@ -35,6 +36,9 @@ rand = "0.8.5"
|
|||
[build-dependencies]
|
||||
r2r_common = { path = "../r2r_common", version = "0.3.6" }
|
||||
r2r_msg_gen = { path = "../r2r_msg_gen", version = "0.3.11" }
|
||||
rayon = "1.7.0"
|
||||
force-send-sync = "1.0.0"
|
||||
quote = "1.0.28"
|
||||
|
||||
[features]
|
||||
save-bindgen = ["r2r_rcl/save-bindgen", "r2r_msg_gen/save-bindgen", "r2r_actions/save-bindgen"]
|
||||
|
|
|
|||
277
r2r/build.rs
277
r2r/build.rs
|
|
@ -1,7 +1,10 @@
|
|||
use quote::format_ident;
|
||||
use quote::quote;
|
||||
use rayon::prelude::*;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::io::{self, prelude::*, BufWriter};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, fs};
|
||||
use std::{env, fmt, fs};
|
||||
|
||||
const LIST_FILENAME: &str = "files.txt";
|
||||
const MSGS_FILENAME: &str = "_r2r_generated_msgs.rs";
|
||||
|
|
@ -50,106 +53,173 @@ fn main() {
|
|||
}
|
||||
|
||||
fn generate_bindings(bindgen_dir: &Path) {
|
||||
fs::create_dir_all(&bindgen_dir).unwrap();
|
||||
fs::create_dir_all(bindgen_dir).unwrap();
|
||||
|
||||
let msg_list = r2r_common::get_wanted_messages();
|
||||
let msgs = r2r_common::as_map(&msg_list);
|
||||
|
||||
let mut modules = String::new();
|
||||
{
|
||||
let modules = {
|
||||
let mut modules: Vec<_> = msgs
|
||||
.par_iter()
|
||||
.map(|(module, _)| {
|
||||
let path_suffix = format!("/{module}.rs");
|
||||
let module_ident = format_ident!("{module}");
|
||||
let tokens = quote! {
|
||||
pub mod #module_ident {
|
||||
include!(concat!(env!("OUT_DIR"), #path_suffix));
|
||||
}
|
||||
};
|
||||
(module, unsafe { force_send(tokens) })
|
||||
})
|
||||
.collect();
|
||||
modules.par_sort_unstable_by_key(|(module, _)| *module);
|
||||
let modules = modules.into_iter().map(|(_, tokens)| tokens.unwrap());
|
||||
|
||||
quote! { #(#modules)* }
|
||||
};
|
||||
|
||||
let msgs_file = bindgen_dir.join(MSGS_FILENAME);
|
||||
write_to_file(&msgs_file, &modules).unwrap();
|
||||
}
|
||||
|
||||
let mod_files: Vec<_> = msgs
|
||||
.iter()
|
||||
.par_iter()
|
||||
.map(|(module, prefixes)| {
|
||||
let mod_text = format!(
|
||||
r#"pub mod {module}{{include!(concat!(env!("OUT_DIR"), "/{module}.rs"));}}{lf}"#,
|
||||
module = module,
|
||||
lf = "\n"
|
||||
);
|
||||
modules.push_str(&mod_text);
|
||||
let snipplets: Vec<_> = prefixes
|
||||
.into_par_iter()
|
||||
.map(|(prefix, msgs)| {
|
||||
let prefix_content = match *prefix {
|
||||
"msg" => {
|
||||
let msg_snipplets = msgs.into_iter().map(|msg| {
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
r2r_msg_gen::generate_rust_msg(module, prefix, msg)
|
||||
});
|
||||
|
||||
let mut codegen = String::new();
|
||||
|
||||
prefixes.into_iter().for_each(|(prefix, msgs)| {
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", prefix));
|
||||
|
||||
if prefix == &"action" {
|
||||
for msg in msgs {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", msg));
|
||||
codegen.push_str(" use super::super::super::*;\n");
|
||||
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_action(module, prefix, msg));
|
||||
|
||||
for s in &["Goal", "Result", "Feedback"] {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module, prefix, &msgname,
|
||||
));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
}
|
||||
|
||||
// "internal" services that implements the action type
|
||||
for srv in &["SendGoal", "GetResult"] {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", srv));
|
||||
codegen.push_str(" use super::super::super::super::*;\n");
|
||||
|
||||
let srvname = format!("{}_{}", msg, srv);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_service(
|
||||
module, prefix, &srvname,
|
||||
));
|
||||
|
||||
for s in &["Request", "Response"] {
|
||||
let msgname = format!("{}_{}_{}", msg, srv, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module, prefix, &msgname,
|
||||
));
|
||||
quote! {
|
||||
use super::super::*;
|
||||
#(#msg_snipplets)*
|
||||
}
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
"srv" => {
|
||||
let msg_snipplets = msgs.into_iter().map(|msg| {
|
||||
let service_snipplet =
|
||||
r2r_msg_gen::generate_rust_service(module, prefix, msg);
|
||||
let msg_snipplets = ["Request", "Response"].iter().map(|s| {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
println!(
|
||||
"cargo:rustc-cfg=r2r__{}__{}__{}",
|
||||
module, prefix, msg
|
||||
);
|
||||
r2r_msg_gen::generate_rust_msg(module, prefix, &msgname)
|
||||
});
|
||||
let msg = format_ident!("{msg}");
|
||||
|
||||
// also "internal" feedback message type that wraps the feedback type with a uuid
|
||||
let feedback_msgname = format!("{}_FeedbackMessage", msg);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module,
|
||||
prefix,
|
||||
&feedback_msgname,
|
||||
));
|
||||
quote! {
|
||||
#[allow(non_snake_case)]
|
||||
pub mod #msg {
|
||||
use super::super::super::*;
|
||||
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
} else if prefix == &"srv" {
|
||||
for msg in msgs {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", msg));
|
||||
codegen.push_str(" use super::super::super::*;\n");
|
||||
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_service(module, prefix, msg));
|
||||
|
||||
for s in &["Request", "Response"] {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module, prefix, &msgname,
|
||||
));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
#service_snipplet
|
||||
#(#msg_snipplets)*
|
||||
}
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
#(#msg_snipplets)*
|
||||
}
|
||||
}
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
} else if prefix == &"msg" {
|
||||
codegen.push_str(" use super::super::*;\n");
|
||||
for msg in msgs {
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(module, prefix, msg));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
}
|
||||
} else {
|
||||
panic!("unknown prefix type: {}", prefix);
|
||||
}
|
||||
"action" => {
|
||||
let msg_snipplets = msgs.into_iter().map(|msg| {
|
||||
let action_snipplet =
|
||||
r2r_msg_gen::generate_rust_action(module, prefix, msg);
|
||||
|
||||
codegen.push_str(" }\n");
|
||||
});
|
||||
let msg_snipplets =
|
||||
["Goal", "Result", "Feedback"].iter().map(|s| {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
println!(
|
||||
"cargo:rustc-cfg=r2r__{}__{}__{}",
|
||||
module, prefix, msg
|
||||
);
|
||||
r2r_msg_gen::generate_rust_msg(module, prefix, &msgname)
|
||||
});
|
||||
|
||||
// "internal" services that implements the action type
|
||||
let service_snipplets =
|
||||
["SendGoal", "GetResult"].iter().map(|srv| {
|
||||
let srvname = format!("{}_{}", msg, srv);
|
||||
let service_snipplet = r2r_msg_gen::generate_rust_service(
|
||||
module, prefix, &srvname,
|
||||
);
|
||||
|
||||
let msg_snipplets =
|
||||
["Request", "Response"].iter().map(|s| {
|
||||
let msgname = format!("{}_{}_{}", msg, srv, s);
|
||||
r2r_msg_gen::generate_rust_msg(
|
||||
module, prefix, &msgname,
|
||||
)
|
||||
});
|
||||
|
||||
let srv = format_ident!("{srv}");
|
||||
quote! {
|
||||
#[allow(non_snake_case)]
|
||||
pub mod #srv {
|
||||
use super::super::super::super::*;
|
||||
|
||||
#service_snipplet
|
||||
#(#msg_snipplets)*
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// also "internal" feedback message type that wraps the feedback type with a uuid
|
||||
let feedback_msgname = format!("{}_FeedbackMessage", msg);
|
||||
let feedback_msg_snipplet = r2r_msg_gen::generate_rust_msg(
|
||||
module,
|
||||
prefix,
|
||||
&feedback_msgname,
|
||||
);
|
||||
|
||||
let msg = format_ident!("{msg}");
|
||||
quote! {
|
||||
#[allow(non_snake_case)]
|
||||
pub mod #msg {
|
||||
use super::super::super::*;
|
||||
|
||||
#action_snipplet
|
||||
#(#msg_snipplets)*
|
||||
#(#service_snipplets)*
|
||||
#feedback_msg_snipplet
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#(#msg_snipplets)*
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("unknown prefix type: {}", prefix);
|
||||
}
|
||||
};
|
||||
|
||||
let prefix = format_ident!("{prefix}");
|
||||
|
||||
let mod_content = quote! {
|
||||
pub mod #prefix {
|
||||
#prefix_content
|
||||
}
|
||||
};
|
||||
|
||||
unsafe { force_send(mod_content) }
|
||||
})
|
||||
.collect();
|
||||
|
||||
let snipplets = snipplets.into_iter().map(|snipplet| snipplet.unwrap());
|
||||
let mod_content = quote! { #(#snipplets)* };
|
||||
let file_name = format!("{}.rs", module);
|
||||
let mod_file = bindgen_dir.join(&file_name);
|
||||
fs::write(&mod_file, codegen).unwrap();
|
||||
write_to_file(&mod_file, &mod_content).unwrap();
|
||||
|
||||
file_name
|
||||
})
|
||||
|
|
@ -158,24 +228,26 @@ fn generate_bindings(bindgen_dir: &Path) {
|
|||
// Write helper files
|
||||
{
|
||||
let untyped_helper = r2r_msg_gen::generate_untyped_helper(&msg_list);
|
||||
let untyped_service_helper = r2r_msg_gen::generate_untyped_service_helper(&msg_list);
|
||||
let untyped_action_helper = r2r_msg_gen::generate_untyped_action_helper(&msg_list);
|
||||
|
||||
let msgs_file = bindgen_dir.join(MSGS_FILENAME);
|
||||
let untyped_file = bindgen_dir.join(UNTYPED_FILENAME);
|
||||
let untyped_service_file = bindgen_dir.join(UNTYPED_SERVICE_FILENAME);
|
||||
let untyped_action_file = bindgen_dir.join(UNTYPED_ACTION_FILENAME);
|
||||
write_to_file(&untyped_file, &untyped_helper).unwrap();
|
||||
}
|
||||
|
||||
fs::write(&msgs_file, &modules).unwrap();
|
||||
fs::write(&untyped_file, &untyped_helper).unwrap();
|
||||
fs::write(&untyped_service_file, &untyped_service_helper).unwrap();
|
||||
fs::write(&untyped_action_file, &untyped_action_helper).unwrap();
|
||||
{
|
||||
let untyped_service_helper = r2r_msg_gen::generate_untyped_service_helper(&msg_list);
|
||||
let untyped_service_file = bindgen_dir.join(UNTYPED_SERVICE_FILENAME);
|
||||
write_to_file(&untyped_service_file, &untyped_service_helper).unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let untyped_action_helper = r2r_msg_gen::generate_untyped_action_helper(&msg_list);
|
||||
let untyped_action_file = bindgen_dir.join(UNTYPED_ACTION_FILENAME);
|
||||
write_to_file(&untyped_action_file, &untyped_action_helper).unwrap();
|
||||
}
|
||||
|
||||
// Save file list
|
||||
{
|
||||
let list_file = bindgen_dir.join(LIST_FILENAME);
|
||||
let mut writer = File::create(list_file).unwrap();
|
||||
let mut writer = BufWriter::new(File::create(list_file).unwrap());
|
||||
|
||||
for file_name in mod_files {
|
||||
writeln!(writer, "{}", file_name).unwrap();
|
||||
|
|
@ -215,3 +287,14 @@ fn touch(path: &Path) {
|
|||
.open(path)
|
||||
.unwrap_or_else(|_| panic!("Unable to create file '{}'", path.display()));
|
||||
}
|
||||
|
||||
fn write_to_file(path: &Path, content: impl fmt::Display) -> io::Result<()> {
|
||||
let mut writer = BufWriter::new(File::create(path)?);
|
||||
write!(writer, "{}", content)?;
|
||||
writer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn force_send<T>(value: T) -> force_send_sync::Send<T> {
|
||||
force_send_sync::Send::new(value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ documentation = "https://docs.rs/r2r/latest/r2r"
|
|||
[dependencies]
|
||||
bindgen = "0.63.0"
|
||||
itertools = "0.10.5"
|
||||
osstrtools = "0.2.2"
|
||||
sha2 = "0.10.6"
|
||||
os_str_bytes = "6.5.1"
|
||||
|
||||
[features]
|
||||
doc-only = []
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use itertools::Itertools;
|
||||
use osstrtools::OsStrTools;
|
||||
use os_str_bytes::RawOsString;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
|
|
@ -62,10 +62,11 @@ pub fn setup_bindgen_builder() -> bindgen::Builder {
|
|||
}
|
||||
} else if !cfg!(feature = "doc-only") {
|
||||
let ament_prefix_var_name = "AMENT_PREFIX_PATH";
|
||||
let ament_prefix_var = env::var_os(ament_prefix_var_name).expect("Source your ROS!");
|
||||
let ament_prefix_var =
|
||||
RawOsString::new(env::var_os(ament_prefix_var_name).expect("Source your ROS!"));
|
||||
|
||||
for p in ament_prefix_var.split(":") {
|
||||
let path = Path::new(p).join("include");
|
||||
let path = Path::new(&p.to_os_str()).join("include");
|
||||
|
||||
let entries = std::fs::read_dir(path.clone());
|
||||
if let Ok(e) = entries {
|
||||
|
|
@ -138,19 +139,26 @@ pub fn print_cargo_ros_distro() {
|
|||
pub fn print_cargo_link_search() {
|
||||
if env::var_os("CMAKE_INCLUDE_DIRS").is_some() {
|
||||
if let Some(paths) = env::var_os("CMAKE_LIBRARIES") {
|
||||
let paths = RawOsString::new(paths);
|
||||
|
||||
paths
|
||||
.split(":")
|
||||
.into_iter()
|
||||
.filter(|s| s.contains(".so") || s.contains(".dylib"))
|
||||
.flat_map(|l| Path::new(l).parent().and_then(|p| p.to_str()))
|
||||
.filter_map(|l| {
|
||||
let l = l.to_os_str();
|
||||
let parent = Path::new(&l).parent()?;
|
||||
let parent = parent.to_str()?;
|
||||
Some(parent.to_string())
|
||||
})
|
||||
.unique()
|
||||
.for_each(|pp| println!("cargo:rustc-link-search=native={}", pp));
|
||||
}
|
||||
} else {
|
||||
let ament_prefix_var_name = "AMENT_PREFIX_PATH";
|
||||
if let Some(paths) = env::var_os(ament_prefix_var_name) {
|
||||
let paths = RawOsString::new(paths);
|
||||
for path in paths.split(":") {
|
||||
let lib_path = Path::new(path).join("lib");
|
||||
let lib_path = Path::new(&path.to_os_str()).join("lib");
|
||||
if let Some(s) = lib_path.to_str() {
|
||||
println!("cargo:rustc-link-search=native={}", s)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,15 +11,26 @@ repository = "https://github.com/sequenceplanner/r2r"
|
|||
documentation = "https://docs.rs/r2r/latest/r2r"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
r2r_rcl = { path = "../r2r_rcl", version = "0.3.7" }
|
||||
r2r_common = { path = "../r2r_common", version = "0.3.6" }
|
||||
phf = { version = "0.11.1", features = ["macros"] }
|
||||
quote = "1.0.28"
|
||||
proc-macro2 = "1.0.60"
|
||||
syn = "2.0.18"
|
||||
force-send-sync = "1.0.0"
|
||||
rayon = "1.7.0"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.63.0"
|
||||
r2r_rcl = { path = "../r2r_rcl", version = "0.3.7" }
|
||||
r2r_common = { path = "../r2r_common", version = "0.3.6" }
|
||||
heck = "0.4.0"
|
||||
quote = "1.0.28"
|
||||
syn = { version = "2.0.18", features = ["full"] }
|
||||
rayon = "1.7.0"
|
||||
itertools = "0.10.5"
|
||||
force-send-sync = "1.0.0"
|
||||
proc-macro2 = "1.0.60"
|
||||
|
||||
[features]
|
||||
save-bindgen = ["r2r_rcl/save-bindgen"]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
use bindgen::Bindings;
|
||||
use heck::ToSnakeCase;
|
||||
use itertools::chain;
|
||||
use itertools::iproduct;
|
||||
use quote::format_ident;
|
||||
use quote::quote;
|
||||
use r2r_common::RosMsg;
|
||||
|
||||
use rayon::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufWriter;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, fs};
|
||||
use std::collections::HashMap;
|
||||
|
||||
const MSG_INCLUDES_FILENAME: &str = "msg_includes.h";
|
||||
const INTROSPECTION_FILENAME: &str = "introspection_functions.rs";
|
||||
|
|
@ -16,6 +24,8 @@ const GENERATED_FILES: &[&str] = &[
|
|||
CONSTANTS_FILENAME,
|
||||
BINDINGS_FILENAME,
|
||||
];
|
||||
const SRV_SUFFICES: &[&str] = &["Request", "Response"];
|
||||
const ACTION_SUFFICES: &[&str] = &["Goal", "Result", "Feedback", "FeedbackMessage"];
|
||||
|
||||
fn main() {
|
||||
r2r_common::print_cargo_watches();
|
||||
|
|
@ -78,76 +88,178 @@ fn run_bindgen(msg_list: &[RosMsg]) {
|
|||
}
|
||||
|
||||
fn generate_bindings(bindgen_dir: &Path, msg_list: &[RosMsg]) {
|
||||
// Run codegen in parallel.
|
||||
rayon::scope(|scope| {
|
||||
scope.spawn(|_| {
|
||||
let bindings = generate_bindings_file(bindgen_dir);
|
||||
generate_constants(bindgen_dir, msg_list, &bindings);
|
||||
});
|
||||
scope.spawn(|_| {
|
||||
generate_includes(bindgen_dir, msg_list);
|
||||
});
|
||||
scope.spawn(|_| {
|
||||
generate_introspecion_map(bindgen_dir, msg_list);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn generate_includes(bindgen_dir: &Path, msg_list: &[RosMsg]) {
|
||||
let msg_includes_file = bindgen_dir.join(MSG_INCLUDES_FILENAME);
|
||||
let introspection_file = bindgen_dir.join(INTROSPECTION_FILENAME);
|
||||
let constants_file = bindgen_dir.join(CONSTANTS_FILENAME);
|
||||
let bindings_file = bindgen_dir.join(BINDINGS_FILENAME);
|
||||
|
||||
let mut includes = String::new();
|
||||
let mut introspecion_map = String::from("
|
||||
#[cfg(feature = \"doc-only\")]
|
||||
lazy_static! {
|
||||
static ref INTROSPECTION_FNS: HashMap<&'static str, usize> = {
|
||||
HashMap::new()
|
||||
};
|
||||
}
|
||||
// Generate a C include line for each message type.
|
||||
let mut include_lines: Vec<_> = msg_list
|
||||
.par_iter()
|
||||
.flat_map(|msg| {
|
||||
let RosMsg {
|
||||
name,
|
||||
module,
|
||||
prefix,
|
||||
..
|
||||
} = msg;
|
||||
|
||||
#[cfg(not(feature = \"doc-only\"))]
|
||||
lazy_static! {
|
||||
static ref INTROSPECTION_FNS: HashMap<&'static str, usize> = {
|
||||
let mut m = HashMap::new();
|
||||
",
|
||||
);
|
||||
// filename is certainly CamelCase -> snake_case. convert
|
||||
let include_filename = name.to_snake_case();
|
||||
|
||||
for msg in msg_list {
|
||||
// filename is certainly CamelCase -> snake_case. convert
|
||||
let include_filename = msg.name.to_snake_case();
|
||||
[
|
||||
format!("#include <{module}/{prefix}/{include_filename}.h>"),
|
||||
format!(
|
||||
"#include <{module}/{prefix}/detail/\
|
||||
{include_filename}__rosidl_typesupport_introspection_c.h>"
|
||||
),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
includes.push_str(&format!(
|
||||
"#include <{}/{}/{}.h>\n",
|
||||
&msg.module, &msg.prefix, &include_filename
|
||||
));
|
||||
includes.push_str(&format!(
|
||||
"#include <{}/{}/detail/{}__rosidl_typesupport_introspection_c.h>\n",
|
||||
&msg.module, &msg.prefix, &include_filename
|
||||
));
|
||||
// Sort the lines.
|
||||
include_lines.par_sort();
|
||||
|
||||
if msg.prefix == "srv" {
|
||||
for s in &["Request", "Response"] {
|
||||
let key = &format!("{}__{}__{}_{}", &msg.module, &msg.prefix, &msg.name, s);
|
||||
let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}_{}() }} as *const i32 as usize", &msg.module, &msg.prefix, &msg.name, s);
|
||||
introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val));
|
||||
}
|
||||
} else if msg.prefix == "action" {
|
||||
for s in &["Goal", "Result", "Feedback", "FeedbackMessage"] {
|
||||
let key = &format!("{}__{}__{}_{}", &msg.module, &msg.prefix, &msg.name, s);
|
||||
let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}_{}() }} as *const i32 as usize", &msg.module, &msg.prefix, &msg.name, s);
|
||||
introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val));
|
||||
}
|
||||
// "internal" services
|
||||
for srv in &["SendGoal", "GetResult"] {
|
||||
// TODO: refactor this is copy paste from services...
|
||||
for s in &["Request", "Response"] {
|
||||
let msgname = format!("{}_{}_{}", msg.name, srv, s);
|
||||
let key = &format!("{}__{}__{}", &msg.module, &msg.prefix, msgname);
|
||||
let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}() }} as *const i32 as usize", &msg.module, &msg.prefix, msgname);
|
||||
introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let key = &format!("{}__{}__{}", &msg.module, &msg.prefix, &msg.name);
|
||||
let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}() }} as *const i32 as usize", &msg.module, &msg.prefix, &msg.name);
|
||||
introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val));
|
||||
}
|
||||
// Write the file content
|
||||
let mut writer = BufWriter::new(File::create(&msg_includes_file).unwrap());
|
||||
for line in include_lines {
|
||||
writeln!(writer, "{line}").unwrap();
|
||||
}
|
||||
introspecion_map.push_str("m \n }; }\n\n");
|
||||
}
|
||||
|
||||
fs::write(&msg_includes_file, includes).unwrap();
|
||||
fs::write(&introspection_file, introspecion_map).unwrap();
|
||||
fn generate_introspecion_map(bindgen_dir: &Path, msg_list: &[RosMsg]) {
|
||||
let introspection_file = bindgen_dir.join(INTROSPECTION_FILENAME);
|
||||
|
||||
let mut entries: Vec<_> = msg_list
|
||||
.par_iter()
|
||||
.flat_map(|msg| {
|
||||
let RosMsg {
|
||||
module,
|
||||
prefix,
|
||||
name,
|
||||
} = msg;
|
||||
|
||||
match prefix.as_str() {
|
||||
"srv" => SRV_SUFFICES
|
||||
.iter()
|
||||
.map(|s| {
|
||||
let key = format!("{module}__{prefix}__{name}_{s}");
|
||||
let ident = format!(
|
||||
"rosidl_typesupport_introspection_c__get_message_type_support_handle__\
|
||||
{module}__\
|
||||
{prefix}__\
|
||||
{name}_\
|
||||
{s}"
|
||||
);
|
||||
(key, ident)
|
||||
})
|
||||
.map(|(key, ident)| (key, ident))
|
||||
.collect(),
|
||||
"action" => {
|
||||
let iter1 = ACTION_SUFFICES.iter().map(|s| {
|
||||
let key = format!("{module}__{prefix}__{name}_{s}");
|
||||
let ident = format!(
|
||||
"rosidl_typesupport_introspection_c__\
|
||||
get_message_type_support_handle__\
|
||||
{module}__\
|
||||
{prefix}__\
|
||||
{name}_\
|
||||
{s}",
|
||||
);
|
||||
(key, ident)
|
||||
});
|
||||
|
||||
// "internal" services
|
||||
let iter2 =
|
||||
iproduct!(["SendGoal", "GetResult"], SRV_SUFFICES).map(move |(srv, s)| {
|
||||
// TODO: refactor this is copy paste from services...
|
||||
let msgname = format!("{name}_{srv}_{s}");
|
||||
let key = format!("{module}__{prefix}__{msgname}");
|
||||
let ident = format!(
|
||||
"rosidl_typesupport_introspection_c__\
|
||||
get_message_type_support_handle__\
|
||||
{module}__\
|
||||
{prefix}__\
|
||||
{msgname}"
|
||||
);
|
||||
(key, ident)
|
||||
});
|
||||
|
||||
chain!(iter1, iter2)
|
||||
.map(|(key, ident)| (key, ident))
|
||||
.collect()
|
||||
}
|
||||
"msg" => {
|
||||
let key = format!("{module}__{prefix}__{name}");
|
||||
let ident = format!(
|
||||
"rosidl_typesupport_introspection_c__\
|
||||
get_message_type_support_handle__\
|
||||
{module}__\
|
||||
{prefix}__\
|
||||
{name}"
|
||||
);
|
||||
vec![(key, ident)]
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
.map(|(key, func_str)| {
|
||||
// Generate a hashmap entry
|
||||
let func_ident = format_ident!("{func_str}");
|
||||
let tokens = quote! {
|
||||
#key =>
|
||||
#func_ident
|
||||
as unsafe extern "C" fn() -> *const rosidl_message_type_support_t
|
||||
};
|
||||
|
||||
// force_send to workaround !Send
|
||||
(key, unsafe { force_send(tokens) })
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort the entries by key
|
||||
entries.par_sort_by_cached_key(|(key, _)| key.to_string());
|
||||
|
||||
let entries = entries.into_iter().map(|(_, tokens)| tokens.unwrap());
|
||||
|
||||
// Write the file content
|
||||
let introspecion_map = quote! {
|
||||
type IntrospectionFn = unsafe extern "C" fn() -> *const rosidl_message_type_support_t;
|
||||
|
||||
static INTROSPECTION_FNS: phf::Map<&'static str, IntrospectionFn> = phf::phf_map! {
|
||||
#(#entries),*
|
||||
};
|
||||
};
|
||||
|
||||
let mut writer = BufWriter::new(File::create(introspection_file).unwrap());
|
||||
writeln!(&mut writer, "{}", introspecion_map).unwrap();
|
||||
}
|
||||
|
||||
fn generate_bindings_file(bindgen_dir: &Path) -> Bindings {
|
||||
let msg_includes_file = bindgen_dir.join(MSG_INCLUDES_FILENAME);
|
||||
let bindings_file = bindgen_dir.join(BINDINGS_FILENAME);
|
||||
|
||||
let builder = r2r_common::setup_bindgen_builder()
|
||||
.header(msg_includes_file.to_str().unwrap())
|
||||
.derive_copy(false)
|
||||
.allowlist_function("rosidl_typesupport_c__.*")
|
||||
.allowlist_function("rosidl_typesupport_introspection_c__.*")
|
||||
.allowlist_function(r"[\w_]*__(msg|srv|action)__[\w_]*__(create|destroy)")
|
||||
.allowlist_function(r"[\w_]*__(msg|srv|action)__[\w_]*__Sequence__(init|fini)")
|
||||
.allowlist_var(r"[\w_]*__(msg|srv|action)__[\w_]*__[\w_]*")
|
||||
// blacklist types that are handled by rcl bindings
|
||||
.blocklist_type("rosidl_message_type_support_t")
|
||||
.blocklist_type("rosidl_service_type_support_t")
|
||||
|
|
@ -181,75 +293,178 @@ fn generate_bindings(bindgen_dir: &Path, msg_list: &[RosMsg]) {
|
|||
});
|
||||
|
||||
let bindings = builder.generate().expect("Unable to generate bindings");
|
||||
|
||||
// Let's add a hack to generate constants.
|
||||
let str_bindings = bindings.to_string();
|
||||
// find all lines which look suspiciosly like a constant.
|
||||
let mut constants: HashMap<String, Vec<(String,String)>> = HashMap::new();
|
||||
for msg in msg_list {
|
||||
if msg.prefix == "srv" {
|
||||
for s in &["Request", "Response"] {
|
||||
let key = format!("{}__{}__{}_{}", &msg.module, &msg.prefix, &msg.name, s);
|
||||
add_constants(&key, &str_bindings, &mut constants);
|
||||
}
|
||||
} else if msg.prefix == "action" {
|
||||
for s in &["Goal", "Result", "Feedback", "FeedbackMessage"] {
|
||||
let key = format!("{}__{}__{}_{}", &msg.module, &msg.prefix, &msg.name, s);
|
||||
add_constants(&key, &str_bindings, &mut constants);
|
||||
}
|
||||
} else {
|
||||
let key = format!("{}__{}__{}", &msg.module, &msg.prefix, &msg.name);
|
||||
add_constants(&key, &str_bindings, &mut constants);
|
||||
}
|
||||
}
|
||||
// generate a constant which holds all constants.
|
||||
let mut constants_map = String::from(
|
||||
"\
|
||||
lazy_static! {
|
||||
static ref CONSTANTS_MAP: HashMap<&'static str, Vec<(String,String)>> = {
|
||||
let mut m = HashMap::new();\
|
||||
");
|
||||
for (msg,msg_constants) in constants {
|
||||
let msg_constants_str = format!("vec![{}]",
|
||||
msg_constants.iter()
|
||||
.map(|(c,t)| format!("(\"{c}\".to_string(), \"{t}\".to_string())"))
|
||||
.collect::<Vec<String>>().join(","));
|
||||
|
||||
constants_map.push_str(&format!("m.insert(\"{}\", {});\n", msg, msg_constants_str));
|
||||
}
|
||||
constants_map.push_str("m \n }; }\n\n");
|
||||
fs::write(&constants_file, constants_map).unwrap();
|
||||
|
||||
bindings
|
||||
.write_to_file(bindings_file)
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
bindings
|
||||
}
|
||||
|
||||
fn add_constants(key: &str, bindings: &str, constants: &mut HashMap<String, Vec<(String, String)>>) {
|
||||
let mut lines = bindings.lines();
|
||||
while let Some(line) = lines.next() {
|
||||
let prefix = format!("pub const {}__", key);
|
||||
if let Some(constant) = line.strip_prefix(&prefix) {
|
||||
if let Some((con, typ)) = constant.split_once(":") {
|
||||
// These are generated automatically for arrays and strings, we don't need to expose them.
|
||||
if con.ends_with("__MAX_SIZE") || con.ends_with("__MAX_STRING_SIZE") {
|
||||
continue;
|
||||
}
|
||||
if let Some((t, _)) = typ.split_once("=") {
|
||||
constants.entry(key.into()).or_default().push((con.to_string(), t.trim().to_string()));
|
||||
} else if let Some(next_line) = lines.next() {
|
||||
// type has moved down to the next line. (bindgen has a max line width)
|
||||
if let Some((t, _)) = next_line.split_once("=") {
|
||||
constants.entry(key.into()).or_default().push((con.to_string(), t.trim().to_string()));
|
||||
} else {
|
||||
panic!("Code generation failure. Type not found in line! {}", next_line);
|
||||
}
|
||||
} else {
|
||||
panic!("Code generation failure. Type not found in line! {}", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn generate_constants(bindgen_dir: &Path, msg_list: &[RosMsg], bindings: &Bindings) {
|
||||
let constants_file = bindgen_dir.join(CONSTANTS_FILENAME);
|
||||
|
||||
// Turn the source string into tokens.
|
||||
let tokens: syn::File =
|
||||
syn::parse_str(&bindings.to_string()).expect("Unable to parse generated bindings");
|
||||
|
||||
// Workaround !Send
|
||||
let items: &[force_send_sync::SendSync<syn::Item>] =
|
||||
unsafe { mem::transmute(tokens.items.as_slice()) };
|
||||
|
||||
/// The key is used to index constant items.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Key {
|
||||
pub module: String,
|
||||
pub prefix: String,
|
||||
pub name: String,
|
||||
|
||||
/// Suffix is None if prefix is "msg". Otherwise, it's not None.
|
||||
pub suffix: Option<String>,
|
||||
}
|
||||
|
||||
// find all lines which look suspiciosly like a constant.
|
||||
let mut constants: Vec<_> = items
|
||||
.par_iter()
|
||||
.filter_map(|item| {
|
||||
// Filter out non-const items.
|
||||
let syn::Item::Const(item) = &**item else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// Filter out constants ending with "__MAX_SIZE" or "__MAX_STRING_SIZE".
|
||||
let ident = item.ident.to_string();
|
||||
if ident.ends_with("__MAX_SIZE") || ident.ends_with("__MAX_STRING_SIZE") {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Create the key for the constant.
|
||||
let (key, const_name) = {
|
||||
let (module, remain) = ident.split_once("__")?;
|
||||
let (prefix, remain) = remain.split_once("__")?;
|
||||
let (name_and_suffix, const_name) = remain.split_once("__")?;
|
||||
let (name, suffix) = match name_and_suffix.rsplit_once('_') {
|
||||
Some((name, suffix)) => (name, Some(suffix.to_string())),
|
||||
None => (name_and_suffix, None),
|
||||
};
|
||||
|
||||
if let Some(suffix) = &suffix {
|
||||
if !SRV_SUFFICES.contains(&suffix.as_str())
|
||||
&& !ACTION_SUFFICES.contains(&suffix.as_str())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let key = Key {
|
||||
module: module.to_string(),
|
||||
prefix: prefix.to_string(),
|
||||
name: name.to_string(),
|
||||
suffix,
|
||||
};
|
||||
|
||||
(key, const_name)
|
||||
};
|
||||
|
||||
// Generate the entry for the constant.
|
||||
let typ = &item.ty;
|
||||
let entry = (const_name.to_string(), quote! { #typ }.to_string());
|
||||
Some((key, entry))
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort the constants to enable later binary range search.
|
||||
constants.par_sort_unstable();
|
||||
|
||||
let mut entries: Vec<_> = msg_list
|
||||
.par_iter()
|
||||
.flat_map(|msg| {
|
||||
// Generate a key for each message type.
|
||||
|
||||
let RosMsg {
|
||||
module,
|
||||
prefix,
|
||||
name,
|
||||
} = msg;
|
||||
|
||||
match prefix.as_str() {
|
||||
"msg" => vec![Key {
|
||||
module: module.to_string(),
|
||||
prefix: prefix.to_string(),
|
||||
name: name.to_string(),
|
||||
suffix: None,
|
||||
}],
|
||||
"srv" => SRV_SUFFICES
|
||||
.iter()
|
||||
.map(|suffix| Key {
|
||||
module: module.to_string(),
|
||||
prefix: prefix.to_string(),
|
||||
name: name.to_string(),
|
||||
suffix: Some(suffix.to_string()),
|
||||
})
|
||||
.collect(),
|
||||
"action" => ACTION_SUFFICES
|
||||
.iter()
|
||||
.map(|suffix| Key {
|
||||
module: module.to_string(),
|
||||
prefix: prefix.to_string(),
|
||||
name: name.to_string(),
|
||||
suffix: Some(suffix.to_string()),
|
||||
})
|
||||
.collect(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
.filter_map(|key| {
|
||||
// Search for items with the same key using binary searches.
|
||||
let range = {
|
||||
let idx = constants.partition_point(|(other, _)| other < &key);
|
||||
let len = constants
|
||||
.get(idx..)?
|
||||
.partition_point(|(other, _)| other == &key);
|
||||
if len == 0 {
|
||||
return None;
|
||||
}
|
||||
idx..(idx + len)
|
||||
};
|
||||
|
||||
let Key {
|
||||
module,
|
||||
prefix,
|
||||
name,
|
||||
suffix,
|
||||
} = key;
|
||||
let msg = match suffix {
|
||||
Some(suffix) => format!("{module}__{prefix}__{name}_{suffix}"),
|
||||
None => format!("{module}__{prefix}__{name}"),
|
||||
};
|
||||
|
||||
Some((msg, &constants[range]))
|
||||
})
|
||||
.map(|(msg, msg_constants)| {
|
||||
// Generate map entries.
|
||||
let consts = msg_constants
|
||||
.iter()
|
||||
.map(|(_, (const_name, typ))| quote! { (#const_name, #typ) });
|
||||
let entry = quote! { #msg => &[ #(#consts),* ] };
|
||||
|
||||
// Workaround !Send
|
||||
(msg, unsafe { force_send(entry) })
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort entries by message name
|
||||
entries.par_sort_by_cached_key(|(msg, _)| msg.to_string());
|
||||
let entries = entries.into_iter().map(|(_, tokens)| tokens.unwrap());
|
||||
|
||||
// Write the file content.
|
||||
let constants_map = quote! {
|
||||
static CONSTANTS_MAP: phf::Map<&'static str, &[(&str, &str)]> = phf::phf_map! {
|
||||
#(#entries),*
|
||||
};
|
||||
};
|
||||
|
||||
let mut writer = BufWriter::new(File::create(constants_file).unwrap());
|
||||
writeln!(&mut writer, "{}", constants_map).unwrap();
|
||||
}
|
||||
|
||||
fn run_dynlink(#[allow(unused_variables)] msg_list: &[RosMsg]) {
|
||||
|
|
@ -276,3 +491,7 @@ fn touch(path: &Path) {
|
|||
.open(path)
|
||||
.unwrap_or_else(|_| panic!("Unable to create file '{}'", path.display()));
|
||||
}
|
||||
|
||||
unsafe fn force_send<T>(value: T) -> force_send_sync::Send<T> {
|
||||
force_send_sync::Send::new(value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,252 @@
|
|||
use crate::rosidl_message_type_support_t as CTypeSupport;
|
||||
use crate::rosidl_typesupport_introspection_c__MessageMember as CMessageMember;
|
||||
use crate::rosidl_typesupport_introspection_c__MessageMembers as CMessageMembers;
|
||||
use crate::rust_mangle;
|
||||
use quote::quote;
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::mem;
|
||||
use std::slice;
|
||||
|
||||
pub struct Introspection<'a> {
|
||||
pub module: &'a str,
|
||||
pub prefix: &'a str,
|
||||
pub name: &'a str,
|
||||
pub members: &'a [MessageMember],
|
||||
}
|
||||
|
||||
impl<'a> Introspection<'a> {
|
||||
pub fn c_struct_name(&self) -> String {
|
||||
let Self {
|
||||
module,
|
||||
prefix,
|
||||
name,
|
||||
..
|
||||
} = *self;
|
||||
format!("{module}__{prefix}__{name}")
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct TypeSupport(CTypeSupport);
|
||||
|
||||
impl TypeSupport {
|
||||
pub unsafe fn introspection(&self) -> Introspection<'_> {
|
||||
let members = (self.0.data as *const MessageMembers).as_ref().unwrap();
|
||||
let namespace = members.message_namespace();
|
||||
let name = members.message_name();
|
||||
let members = members.members();
|
||||
|
||||
let (module, remain) = namespace.split_once("__").unwrap();
|
||||
let (prefix, _) = remain.split_once("__").unwrap();
|
||||
|
||||
Introspection {
|
||||
module,
|
||||
prefix,
|
||||
name,
|
||||
members,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct MessageMember(CMessageMember);
|
||||
|
||||
impl MessageMember {
|
||||
pub fn name(&self) -> &str {
|
||||
unsafe { ptr_to_str(self.0.name_) }
|
||||
}
|
||||
|
||||
pub fn rust_name(&self) -> Cow<'_, str> {
|
||||
rust_mangle(self.name())
|
||||
}
|
||||
|
||||
pub fn type_id(&self) -> MemberType {
|
||||
MemberType::from_type_id(self.0.type_id_).unwrap()
|
||||
}
|
||||
|
||||
pub fn string_upper_bound(&self) -> Option<usize> {
|
||||
if self.type_id() == MemberType::String {
|
||||
Some(self.0.string_upper_bound_ as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn members(&self) -> Option<&TypeSupport> {
|
||||
if self.type_id() != MemberType::Message {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let ptr = self.0.members_ as *const TypeSupport;
|
||||
let ref_ = ptr.as_ref().unwrap();
|
||||
Some(ref_)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_array(&self) -> bool {
|
||||
self.0.is_array_
|
||||
}
|
||||
|
||||
pub fn array_size(&self) -> Option<usize> {
|
||||
if self.is_array() {
|
||||
Some(self.0.array_size_)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_upper_bound(&self) -> Option<bool> {
|
||||
if self.is_array() {
|
||||
Some(self.0.is_upper_bound_)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn array_info(&self) -> Option<ArrayInfo> {
|
||||
self.0.is_array_.then(|| ArrayInfo {
|
||||
size: self.0.array_size_,
|
||||
is_upper_bound: self.0.is_upper_bound_,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> usize {
|
||||
self.0.offset_ as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct MessageMembers(CMessageMembers);
|
||||
|
||||
impl MessageMembers {
|
||||
pub fn message_namespace(&self) -> &str {
|
||||
unsafe { ptr_to_str(self.0.message_namespace_) }
|
||||
}
|
||||
|
||||
pub fn message_name(&self) -> &str {
|
||||
unsafe { ptr_to_str(self.0.message_name_) }
|
||||
}
|
||||
|
||||
pub fn member_count(&self) -> usize {
|
||||
self.0.member_count_ as usize
|
||||
}
|
||||
|
||||
pub fn size_of(&self) -> usize {
|
||||
self.0.size_of_ as usize
|
||||
}
|
||||
|
||||
pub fn members(&self) -> &[MessageMember] {
|
||||
unsafe {
|
||||
let members: &[CMessageMember] =
|
||||
slice::from_raw_parts(self.0.members_, self.member_count());
|
||||
mem::transmute(members)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MemberType {
|
||||
Bool,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
F32,
|
||||
F64,
|
||||
Char,
|
||||
WChar,
|
||||
String,
|
||||
WString,
|
||||
Message,
|
||||
}
|
||||
|
||||
impl MemberType {
|
||||
/// Get the enum variant for the type id.
|
||||
///
|
||||
/// Because the c enum has been named between galactic and the
|
||||
/// next release, we cannot know its name. therefor we use the
|
||||
/// constants as is and hope we notice when they change.
|
||||
///
|
||||
/// ```ignore
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_FLOAT = 1,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_DOUBLE = 2,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_LONG_DOUBLE = 3,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_CHAR = 4,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_WCHAR = 5,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_BOOLEAN = 6,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_OCTET = 7,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_UINT8 = 8,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_INT8 = 9,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_UINT16 = 10,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_INT16 = 11,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_UINT32 = 12,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_INT32 = 13,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_UINT64 = 14,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_INT64 = 15,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_STRING = 16,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_WSTRING = 17,
|
||||
/// rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE = 18,
|
||||
/// ```
|
||||
pub fn from_type_id(id: u8) -> Option<Self> {
|
||||
Some(match id {
|
||||
1 => Self::F32,
|
||||
2 => Self::F64,
|
||||
3 => Self::U128,
|
||||
4 => Self::Char,
|
||||
5 => Self::WChar,
|
||||
6 => Self::Bool,
|
||||
7 | 8 => Self::U8,
|
||||
9 => Self::I8,
|
||||
10 => Self::U16,
|
||||
11 => Self::I16,
|
||||
12 => Self::U32,
|
||||
13 => Self::I32,
|
||||
14 => Self::U64,
|
||||
15 => Self::I64,
|
||||
16 => Self::String,
|
||||
17 => Self::WString,
|
||||
18 => Self::Message,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_rust_type(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
MemberType::Bool => quote! { bool },
|
||||
MemberType::I8 => quote! { i8 },
|
||||
MemberType::I16 => quote! { i16 },
|
||||
MemberType::I32 => quote! { i32 },
|
||||
MemberType::I64 => quote! { i64 },
|
||||
MemberType::U8 => quote! { u8 },
|
||||
MemberType::U16 => quote! { u16 },
|
||||
MemberType::U32 => quote! { u32 },
|
||||
MemberType::U64 => quote! { u64 },
|
||||
MemberType::U128 => quote! { u128 },
|
||||
MemberType::F32 => quote! { f32 },
|
||||
MemberType::F64 => quote! { f64 },
|
||||
MemberType::Char => quote! { std::ffi::c_char },
|
||||
MemberType::WChar => quote! { widestring::WideChar },
|
||||
MemberType::String => quote! { std::string::String },
|
||||
MemberType::WString => quote! { std::string::String },
|
||||
MemberType::Message => quote! { message },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ArrayInfo {
|
||||
pub size: usize,
|
||||
pub is_upper_bound: bool,
|
||||
}
|
||||
|
||||
unsafe fn ptr_to_str(ptr: *const c_char) -> &'static str {
|
||||
CStr::from_ptr(ptr).to_str().unwrap()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue