diff --git a/rclrs/src/action/action_client/goal_client.rs b/rclrs/src/action/action_client/goal_client.rs index 61d2773f..c5171229 100644 --- a/rclrs/src/action/action_client/goal_client.rs +++ b/rclrs/src/action/action_client/goal_client.rs @@ -95,6 +95,8 @@ impl GoalClient { /// # Example /// /// ``` +/// # #[cfg(doctest)] +/// # { /// use rclrs::*; /// use crate::rclrs::vendor::example_interfaces::action::Fibonacci; /// use futures::StreamExt; @@ -118,6 +120,7 @@ impl GoalClient { /// } /// } /// } +/// # } /// ``` pub struct GoalClientStream { stream_map: StreamMap> + Send>>>, diff --git a/rclrs/src/client.rs b/rclrs/src/client.rs index e5fb1087..07d0719f 100644 --- a/rclrs/src/client.rs +++ b/rclrs/src/client.rs @@ -162,6 +162,8 @@ where /// Define an `async fn` whose arguments are compatible with one of the above /// signatures and which returns a `()` (a.k.a. nothing). /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # let node = Context::default() @@ -176,6 +178,7 @@ where /// let request = test_msgs::srv::Empty_Request::default(); /// let promise = client.call_then_async(&request, print_hello)?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// /// ## 2. Function that returns an `async { ... }` @@ -190,6 +193,8 @@ where /// ### `fn` /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # use std::future::Future; @@ -210,6 +215,7 @@ where /// &request, /// print_greeting)?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// /// ### Closure @@ -219,6 +225,8 @@ where /// is also the most powerful option. /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # let node = Context::default() @@ -236,6 +244,7 @@ where /// } /// })?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` pub fn call_then_async<'a, Req, Args>( &self, diff --git a/rclrs/src/lib.rs b/rclrs/src/lib.rs index 9db4767f..5a9876ab 100644 --- a/rclrs/src/lib.rs +++ b/rclrs/src/lib.rs @@ -30,7 +30,9 @@ //! [`Executor`], and then a [`Node`]. Create whatever primitives you need, and //! then tell the [`Executor`] to spin: //! -//! ```no_run +//! ``` +//! # #[cfg(doctest)] +//! # { //! use rclrs::*; //! # use crate::rclrs::vendor::example_interfaces; //! @@ -47,13 +49,16 @@ //! //! executor.spin(SpinOptions::default()).first_error()?; //! # Ok::<(), RclrsError>(()) +//! # } //! ``` //! //! If your callback needs to interact with some state data, consider using a //! [`Worker`], especially if that state data needs to be shared with other //! callbacks: //! -//! ```no_run +//! ``` +//! # #[cfg(doctest)] +//! # { //! # use rclrs::*; //! # //! # let context = Context::default_from_env()?; @@ -84,6 +89,7 @@ //! //! # executor.spin(SpinOptions::default()).first_error()?; //! # Ok::<(), RclrsError>(()) +//! # } //! ``` //! //! # Parameters @@ -97,7 +103,9 @@ //! - Read-only parameters do not allow you to modify them after they have been declared. //! //! The following is a simple example of using a mandatory parameter: -//! ```no_run +//! ``` +//! # #[cfg(doctest)] +//! # { //! use rclrs::*; //! # use crate::rclrs::vendor::example_interfaces; //! use std::sync::Arc; @@ -119,6 +127,7 @@ //! //! executor.spin(SpinOptions::default()).first_error()?; //! # Ok::<(), RclrsError>(()) +//! # } //! ``` //! //! # Logging @@ -127,7 +136,9 @@ //! ergonomic Rust API. [`ToLogParams`] can be used to dictate how logging is //! performed. //! -//! ```no_run +//! ``` +//! # #[cfg(doctest)] +//! # { //! use rclrs::*; //! # use crate::rclrs::vendor::example_interfaces; //! use std::time::Duration; @@ -176,6 +187,7 @@ //! ); //! executor.spin(SpinOptions::default()).first_error()?; //! # Ok::<(), RclrsError>(()) +//! # } //! ``` mod action; diff --git a/rclrs/src/node.rs b/rclrs/src/node.rs index a48ad42f..44581826 100644 --- a/rclrs/src/node.rs +++ b/rclrs/src/node.rs @@ -273,6 +273,8 @@ impl NodeState { /// /// In some cases the payload type can be inferred by Rust: /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// let executor = Context::default().create_basic_executor(); @@ -287,6 +289,7 @@ impl NodeState { /// } /// )?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// /// If the compiler complains about not knowing the payload type, you can @@ -300,7 +303,6 @@ impl NodeState { /// /// ``` /// # use rclrs::*; - /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); /// # let node = executor.create_node("my_node").unwrap(); /// let worker = node.create_worker::(String::new()); @@ -308,6 +310,8 @@ impl NodeState { /// /// The data given to the worker can be any custom data type: /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -320,6 +324,7 @@ impl NodeState { /// } /// /// let worker = node.create_worker(MyNodeData::default()); + /// # } /// ``` /// /// In the above example, `addition_client` and `result_publisher` can be @@ -342,6 +347,8 @@ impl NodeState { /// /// Pass in only the service name for the `options` argument to use all default client options: /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # let executor = Context::default().create_basic_executor(); /// # let node = executor.create_node("my_node").unwrap(); @@ -350,12 +357,15 @@ impl NodeState { /// "my_service" /// ) /// .unwrap(); + /// # } /// ``` /// /// Take advantage of the [`IntoPrimitiveOptions`] API to easily build up the /// client options: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # let executor = Context::default().create_basic_executor(); /// # let node = executor.create_node("my_node").unwrap(); @@ -366,6 +376,7 @@ impl NodeState { /// .transient_local() /// ) /// .unwrap(); + /// # } /// ``` /// /// Any quality of service options that you explicitly specify will override @@ -423,6 +434,8 @@ impl NodeState { /// /// Pass in only the topic name for the `options` argument to use all default publisher options: /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # let executor = Context::default().create_basic_executor(); /// # let node = executor.create_node("my_node").unwrap(); @@ -431,12 +444,15 @@ impl NodeState { /// "my_topic" /// ) /// .unwrap(); + /// # } /// ``` /// /// Take advantage of the [`IntoPrimitiveOptions`] API to easily build up the /// publisher options: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # let executor = Context::default().create_basic_executor(); @@ -453,6 +469,7 @@ impl NodeState { /// .reliable() /// ) /// .unwrap(); + /// # } /// ``` /// pub fn create_publisher<'a, T>( @@ -483,6 +500,7 @@ impl NodeState { /// .keep_last(100) /// ) /// .unwrap(); + /// ``` pub fn create_dynamic_publisher<'a>( self: &Arc, topic_type: MessageTypeName, @@ -510,6 +528,8 @@ impl NodeState { /// /// Pass in only the service name for the `options` argument to use all default service options: /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # let executor = Context::default().create_basic_executor(); @@ -521,12 +541,15 @@ impl NodeState { /// test_msgs::srv::Empty_Response::default() /// }, /// ); + /// # } /// ``` /// /// Take advantage of the [`IntoPrimitiveOptions`] API to easily build up the /// service options: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # let executor = Context::default().create_basic_executor(); @@ -540,6 +563,7 @@ impl NodeState { /// test_msgs::srv::Empty_Response::default() /// }, /// ); + /// # } /// ``` /// /// Any quality of service options that you explicitly specify will override @@ -566,6 +590,8 @@ impl NodeState { /// multiple simultaneous runs of the callback. For example: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -586,12 +612,15 @@ impl NodeState { /// } /// )?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// To share the internal state outside of the callback you will need to /// wrap it in [`Arc`] or `Arc>` and then clone the [`Arc`] before /// capturing it in the closure: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -622,6 +651,7 @@ impl NodeState { /// } /// }); /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// /// In general, when you need to manage some state within a blocking service, @@ -686,6 +716,8 @@ impl NodeState { /// This allows one async service to share state data across multiple workers. /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -721,6 +753,7 @@ impl NodeState { /// } /// )?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` pub fn create_async_service<'a, T, Args>( self: &Arc, @@ -758,6 +791,8 @@ impl NodeState { /// /// Pass in only the topic name for the `options` argument to use all default subscription options: /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # let executor = Context::default().create_basic_executor(); @@ -768,12 +803,15 @@ impl NodeState { /// println!("Received message!"); /// }, /// ); + /// # } /// ``` /// /// Take advantage of the [`IntoPrimitiveOptions`] API to easily build up the /// subscription options: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::test_msgs; /// # let executor = Context::default().create_basic_executor(); @@ -794,6 +832,7 @@ impl NodeState { /// println!("Received message!"); /// }, /// ); + /// # } /// ``` /// /// # Subscription Callbacks @@ -817,6 +856,8 @@ impl NodeState { /// simultaneous runs of the callback. For example: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -833,6 +874,7 @@ impl NodeState { /// }, /// )?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// /// To share the internal state outside of the callback you will need to @@ -840,6 +882,8 @@ impl NodeState { /// the [`Arc`] before capturing it in the closure: /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -865,6 +909,7 @@ impl NodeState { /// } /// }); /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// /// You can change the subscription at any time by calling @@ -1071,6 +1116,8 @@ impl NodeState { /// multiple workers. /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -1105,6 +1152,7 @@ impl NodeState { /// } /// )?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` /// /// You can change the subscription at any time by calling diff --git a/rclrs/src/vendor/mod.rs b/rclrs/src/vendor/mod.rs index 28d3bfd5..e639e833 100644 --- a/rclrs/src/vendor/mod.rs +++ b/rclrs/src/vendor/mod.rs @@ -4,8 +4,10 @@ pub mod action_msgs; pub mod builtin_interfaces; +#[cfg(any(test, doctest))] pub mod example_interfaces; pub mod rcl_interfaces; pub mod rosgraph_msgs; +#[cfg(any(test, doctest))] pub mod test_msgs; pub mod unique_identifier_msgs; diff --git a/rclrs/src/worker.rs b/rclrs/src/worker.rs index 6e390129..907fcad0 100644 --- a/rclrs/src/worker.rs +++ b/rclrs/src/worker.rs @@ -233,6 +233,8 @@ impl WorkerState { /// payload by setting the first argument of the callback to `&mut Payload`. /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -254,6 +256,7 @@ impl WorkerState { /// }, /// )?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` pub fn create_subscription<'a, T, Args>( &self, @@ -383,6 +386,8 @@ impl WorkerState { /// payload by setting the first argument of the callback to `&mut Payload`. /// /// ``` + /// # #[cfg(doctest)] + /// # { /// # use rclrs::*; /// # use crate::rclrs::vendor::example_interfaces; /// # let executor = Context::default().create_basic_executor(); @@ -416,6 +421,7 @@ impl WorkerState { /// } /// )?; /// # Ok::<(), RclrsError>(()) + /// # } /// ``` pub fn create_service<'a, T, Args>( &self, diff --git a/rclrs/vendor_interfaces.py b/rclrs/vendor_interfaces.py index 29b4becc..c4c731b2 100755 --- a/rclrs/vendor_interfaces.py +++ b/rclrs/vendor_interfaces.py @@ -14,13 +14,13 @@ import subprocess vendored_packages = [ - "action_msgs", - "builtin_interfaces", - "example_interfaces", - "rcl_interfaces", - "rosgraph_msgs", - "test_msgs", - "unique_identifier_msgs", + ("action_msgs", False), + ("builtin_interfaces", False), + ("example_interfaces", True), + ("rcl_interfaces", False), + ("rosgraph_msgs", False), + ("test_msgs", True), + ("unique_identifier_msgs", False), ] def get_args(): @@ -30,7 +30,7 @@ def get_args(): return parser.parse_args() def adjust(current_package, text): - for pkg in vendored_packages: + for pkg, _ in vendored_packages: text = text.replace(f'{pkg}::', f'crate::vendor::{pkg}::') text = text.replace('crate::msg', f'crate::vendor::{current_package}::msg') text = text.replace('crate::srv', f'crate::vendor::{current_package}::srv') @@ -44,13 +44,13 @@ def copy_adjusted(pkg, src, dst): def main(): args = get_args() assert args.install_base.is_dir(), "Install base does not exist" - for pkg in vendored_packages: + for pkg, _ in vendored_packages: assert (args.install_base / pkg).is_dir(), f"Install base does not contain {pkg}" rclrs_root = Path(__file__).parent vendor_dir = rclrs_root / 'src' / 'vendor' if vendor_dir.exists(): shutil.rmtree(vendor_dir) - for pkg in vendored_packages: + for pkg, test_only in vendored_packages: src = args.install_base / pkg / 'share' / pkg / 'rust' / 'src' dst = vendor_dir / pkg dst.mkdir(parents=True) @@ -65,7 +65,9 @@ def main(): mod_contents += "#![allow(dead_code)]\n" mod_contents += "#![allow(missing_docs)]\n" mod_contents += "\n" - for pkg in vendored_packages: + for pkg, test_only in vendored_packages: + if test_only: + mod_contents += "#[cfg(any(test, doctest))]\n" mod_contents += f"pub mod {pkg};\n" (vendor_dir / 'mod.rs').write_text(mod_contents)