| @@ -1696,6 +1696,7 @@ name = "dora-ros2-bridge" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "array-init", | |||
| "cxx", | |||
| "dora-daemon", | |||
| "dora-ros2-bridge-msg-gen-macro", | |||
| "eyre", | |||
| @@ -8,6 +8,7 @@ edition = "2021" | |||
| [features] | |||
| default = ["generate-messages"] | |||
| generate-messages = ["dep:dora-ros2-bridge-msg-gen-macro"] | |||
| cxx-bridge = ["dep:cxx"] | |||
| # enables examples that depend on a sourced ROS2 installation | |||
| ros2-examples = ["eyre", "tokio", "dora-daemon"] | |||
| @@ -24,6 +25,7 @@ tokio = { version = "1.29.1", features = ["full"], optional = true } | |||
| dora-daemon = { path = "../../../binaries/daemon", optional = true } | |||
| tracing = "0.1.37" | |||
| tracing-subscriber = "0.3.17" | |||
| cxx = { version = "1.0", optional = true } | |||
| [dev-dependencies] | |||
| rand = "0.8.5" | |||
| @@ -63,14 +63,57 @@ pub fn msg_include_all(input: TokenStream) -> TokenStream { | |||
| .map(Path::new) | |||
| .collect::<Vec<_>>(); | |||
| let message_structs = get_packages(&paths) | |||
| .unwrap() | |||
| .iter() | |||
| .map(|v| v.struct_token_stream(config.create_cxx_bridge)) | |||
| .collect::<Vec<_>>(); | |||
| let aliases = get_packages(&paths) | |||
| .unwrap() | |||
| .iter() | |||
| .map(|v| v.aliases_token_stream()) | |||
| .collect::<Vec<_>>(); | |||
| let packages = get_packages(&paths) | |||
| .unwrap() | |||
| .iter() | |||
| .map(|v| v.token_stream(config.create_cxx_bridge)) | |||
| .collect::<Vec<_>>(); | |||
| let (attributes, imports) = if config.create_cxx_bridge { | |||
| (quote! { #[cxx::bridge] }, quote! {}) | |||
| } else { | |||
| ( | |||
| quote! {}, | |||
| quote! { | |||
| use serde::{Serialize, Deserialize}; | |||
| }, | |||
| ) | |||
| }; | |||
| (quote! { | |||
| #(#packages)* | |||
| #attributes | |||
| pub mod ffi { | |||
| #imports | |||
| #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] | |||
| pub struct U16String { | |||
| chars: Vec<u16>, | |||
| } | |||
| impl crate::_core::InternalDefault for U16String { | |||
| fn _default() -> Self { | |||
| Default::default() | |||
| } | |||
| } | |||
| #(#message_structs)* | |||
| } | |||
| #(#aliases)* | |||
| // #(#packages)* | |||
| }) | |||
| .into() | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| use heck::SnakeCase; | |||
| use quote::{format_ident, quote, ToTokens}; | |||
| use syn::Ident; | |||
| use super::{primitives::*, sequences::Array, ConstantType, MemberType}; | |||
| @@ -122,9 +123,69 @@ pub struct Message { | |||
| } | |||
| impl Message { | |||
| pub fn token_stream_with_mod(&self) -> impl ToTokens { | |||
| pub fn struct_token_stream(&self, package_name: &Ident, gen_cxx_bridge: bool) -> impl ToTokens { | |||
| let cxx_name = format_ident!("{}", self.name); | |||
| let struct_raw_name = format_ident!("{package_name}__{}", self.name); | |||
| let rust_type_def_inner = self.members.iter().map(|m| m.rust_type_def(&self.package)); | |||
| let constants_def_inner = self.constants.iter().map(|c| c.token_stream()); | |||
| let rust_type_default_inner = self.members.iter().map(|m| m.default_value()); | |||
| let attributes = if gen_cxx_bridge { | |||
| quote! { | |||
| #[namespace = #package_name] | |||
| #[cxx_name = #cxx_name] | |||
| } | |||
| } else { | |||
| quote! {} | |||
| }; | |||
| if self.members.is_empty() { | |||
| quote! {} | |||
| } else { | |||
| quote! { | |||
| #[allow(non_camel_case_types)] | |||
| #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | |||
| #attributes | |||
| pub struct #struct_raw_name { | |||
| #(#rust_type_def_inner)* | |||
| } | |||
| impl crate::_core::InternalDefault for #struct_raw_name { | |||
| fn _default() -> Self { | |||
| Self { | |||
| #(#rust_type_default_inner)* | |||
| } | |||
| } | |||
| } | |||
| impl std::default::Default for #struct_raw_name { | |||
| #[inline] | |||
| fn default() -> Self { | |||
| crate::_core::InternalDefault::_default() | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| pub fn alias_token_stream(&self, package_name: &Ident) -> impl ToTokens { | |||
| let cxx_name = format_ident!("{}", self.name); | |||
| let struct_raw_name = format_ident!("{package_name}__{}", self.name); | |||
| if self.members.is_empty() { | |||
| quote! {} | |||
| } else { | |||
| quote! { | |||
| pub use super::super::ffi::#struct_raw_name as #cxx_name; | |||
| } | |||
| } | |||
| } | |||
| pub fn token_stream_with_mod(&self, gen_cxx_bridge: bool) -> impl ToTokens { | |||
| let mod_name = format_ident!("_{}", self.name.to_snake_case()); | |||
| let inner = self.token_stream(); | |||
| let inner = self.token_stream_args(gen_cxx_bridge); | |||
| quote! { | |||
| pub use #mod_name::*; | |||
| mod #mod_name { | |||
| @@ -134,6 +195,10 @@ impl Message { | |||
| } | |||
| pub fn token_stream(&self) -> impl ToTokens { | |||
| self.token_stream_args(false) | |||
| } | |||
| pub fn token_stream_args(&self, gen_cxx_bridge: bool) -> impl ToTokens { | |||
| let rust_type = format_ident!("{}", self.name); | |||
| let raw_type = format_ident!("{}_Raw", self.name); | |||
| let raw_ref_type = format_ident!("{}_RawRef", self.name); | |||
| @@ -144,6 +209,13 @@ impl Message { | |||
| self.members.clone() | |||
| }; | |||
| let attributes = if gen_cxx_bridge { | |||
| let namespace = &self.name; | |||
| quote! { #[cxx::bridge(namespace = #namespace)] } | |||
| } else { | |||
| quote! {} | |||
| }; | |||
| let rust_type_def_inner = self.members.iter().map(|m| m.rust_type_def(&self.package)); | |||
| let constants_def_inner = self.constants.iter().map(|c| c.token_stream()); | |||
| let rust_type_default_inner = self.members.iter().map(|m| m.default_value()); | |||
| @@ -178,10 +250,15 @@ impl Message { | |||
| FFIToRust as _FFIToRust, | |||
| }; | |||
| #[allow(non_camel_case_types)] | |||
| #[derive(std::fmt::Debug, std::clone::Clone, std::cmp::PartialEq, serde::Serialize, serde::Deserialize)] | |||
| pub struct #rust_type { | |||
| #(#rust_type_def_inner)* | |||
| pub use self::t::#rust_type; | |||
| #attributes | |||
| mod t { | |||
| #[allow(non_camel_case_types)] | |||
| #[derive(std::fmt::Debug, std::clone::Clone, std::cmp::PartialEq, serde::Serialize, serde::Deserialize)] | |||
| pub struct #rust_type { | |||
| #(#rust_type_def_inner)* | |||
| } | |||
| } | |||
| impl #rust_type { | |||
| @@ -26,13 +26,50 @@ impl Package { | |||
| self.messages.is_empty() && self.services.is_empty() && self.actions.is_empty() | |||
| } | |||
| fn messages_block(&self) -> impl ToTokens { | |||
| fn message_structs(&self, package_name: Ident, gen_cxx_bridge: bool) -> impl ToTokens { | |||
| if self.messages.is_empty() { | |||
| quote! { | |||
| // empty msg | |||
| } | |||
| } else { | |||
| let items = self.messages.iter().map(|v| v.token_stream_with_mod()); | |||
| let items = self | |||
| .messages | |||
| .iter() | |||
| .map(|v| v.struct_token_stream(&package_name, gen_cxx_bridge)); | |||
| quote! { | |||
| #(#items)* | |||
| } | |||
| } | |||
| } | |||
| fn message_aliases(&self, package_name: &Ident) -> impl ToTokens { | |||
| if self.messages.is_empty() { | |||
| quote! { | |||
| // empty msg | |||
| } | |||
| } else { | |||
| let items = self | |||
| .messages | |||
| .iter() | |||
| .map(|v| v.alias_token_stream(package_name)); | |||
| quote! { | |||
| pub mod msg { | |||
| #(#items)* | |||
| } | |||
| } | |||
| } | |||
| } | |||
| fn messages_block(&self, gen_cxx_bridge: bool) -> impl ToTokens { | |||
| if self.messages.is_empty() { | |||
| quote! { | |||
| // empty msg | |||
| } | |||
| } else { | |||
| let items = self | |||
| .messages | |||
| .iter() | |||
| .map(|v| v.token_stream_with_mod(gen_cxx_bridge)); | |||
| quote! { | |||
| pub mod msg { | |||
| #(#items)* | |||
| @@ -71,21 +108,33 @@ impl Package { | |||
| } | |||
| } | |||
| pub fn struct_token_stream(&self, gen_cxx_bridge: bool) -> impl ToTokens { | |||
| let package_name = Ident::new(&self.name, Span::call_site()); | |||
| let message_structs = self.message_structs(package_name, gen_cxx_bridge); | |||
| quote! { | |||
| #message_structs | |||
| } | |||
| } | |||
| pub fn aliases_token_stream(&self) -> impl ToTokens { | |||
| let package_name = Ident::new(&self.name, Span::call_site()); | |||
| let aliases = self.message_aliases(&package_name); | |||
| quote! { | |||
| pub mod #package_name { | |||
| #aliases | |||
| } | |||
| } | |||
| } | |||
| pub fn token_stream(&self, gen_cxx_bridge: bool) -> impl ToTokens { | |||
| let name = Ident::new(&self.name, Span::call_site()); | |||
| let messages_block = self.messages_block(); | |||
| let messages_block = self.messages_block(gen_cxx_bridge); | |||
| let services_block = self.services_block(); | |||
| let actions_block = self.actions_block(); | |||
| let attributes = if gen_cxx_bridge { | |||
| let namespace = &self.name; | |||
| quote! { #[cxx::bridge(namespace = #namespace)] } | |||
| } else { | |||
| quote! {} | |||
| }; | |||
| quote! { | |||
| #attributes | |||
| pub mod #name { | |||
| #messages_block | |||
| #services_block | |||
| @@ -133,7 +133,8 @@ impl NamedType { | |||
| let package = Ident::new(package, Span::call_site()); | |||
| let namespace = Ident::new("msg", Span::call_site()); | |||
| let name = Ident::new(&self.0, Span::call_site()); | |||
| quote! { crate::#package::#namespace::#name } | |||
| let ident = format_ident!("{package}__{name}"); | |||
| quote! { #ident } | |||
| } | |||
| fn raw_type_tokens(&self, package: &str) -> impl ToTokens { | |||
| @@ -175,7 +176,8 @@ impl NamespacedType { | |||
| let package = Ident::new(&self.package, Span::call_site()); | |||
| let namespace = Ident::new(&self.namespace, Span::call_site()); | |||
| let name = Ident::new(&self.name, Span::call_site()); | |||
| quote! { crate::#package::#namespace::#name } | |||
| let ident = format_ident!("{package}__{name}"); | |||
| quote! { #ident } | |||
| } | |||
| fn raw_type_tokens(&self) -> impl ToTokens { | |||
| @@ -215,9 +217,9 @@ impl GenericString { | |||
| fn type_tokens(self) -> impl ToTokens { | |||
| if self.is_wide() { | |||
| quote! { crate::_core::string::U16String } | |||
| quote! { U16String } | |||
| } else { | |||
| quote! { ::std::string::String } | |||
| quote! { String } | |||
| } | |||
| } | |||
| @@ -41,7 +41,7 @@ pub struct Sequence { | |||
| impl Sequence { | |||
| pub fn type_tokens(&self, package: &str) -> impl ToTokens { | |||
| let inner_type = self.value_type.type_tokens(package); | |||
| quote! { std::vec::Vec<#inner_type> } | |||
| quote! { Vec<#inner_type> } | |||
| } | |||
| pub fn raw_type_tokens(&self, package: &str) -> impl ToTokens { | |||
| @@ -72,7 +72,7 @@ pub struct BoundedSequence { | |||
| impl BoundedSequence { | |||
| pub fn type_tokens(&self, package: &str) -> impl ToTokens { | |||
| let inner_type = self.value_type.type_tokens(package); | |||
| quote! { std::vec::Vec<#inner_type> } | |||
| quote! { Vec<#inner_type> } | |||
| } | |||
| pub fn raw_type_tokens(&self, package: &str) -> impl ToTokens { | |||
| @@ -1,7 +1,10 @@ | |||
| pub use ros2_client; | |||
| pub use rustdds; | |||
| #[cfg(feature = "generate-messages")] | |||
| #[cfg(all(feature = "generate-messages", feature = "cxx-bridge"))] | |||
| dora_ros2_bridge_msg_gen_macro::msg_include_all!(cxx_bridge = true); | |||
| #[cfg(all(feature = "generate-messages", not(feature = "cxx-bridge")))] | |||
| dora_ros2_bridge_msg_gen_macro::msg_include_all!(); | |||
| pub mod _core; | |||