Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3094ff8
Structured parameters initial draft
BCSol Aug 19, 2025
c76651c
Default values for parameters
BCSol Aug 28, 2025
cba5837
Make declare_parameters public
BCSol Aug 30, 2025
80ed497
Move NodeState from parameter options to enabled default derivation
BCSol Aug 30, 2025
2ba7748
Remove parameter struct
BCSol Aug 30, 2025
2561549
Add support for description
BCSol Aug 30, 2025
d9bcf97
Add some documentation
BCSol Aug 30, 2025
0b7ac44
Add module doc with example
BCSol Aug 30, 2025
2d0be3d
Fix format
BCSol Aug 30, 2025
073322c
Revert changes to build
BCSol Aug 30, 2025
c686ba5
Merge remote-tracking branch 'forked/main'
BCSol Aug 30, 2025
4a07678
Fix error message punctuation
BCSol Aug 30, 2025
620d09d
cargo fmt nightly
BCSol Aug 30, 2025
ba3d941
Remove docstring test
balthasarschuess Sep 2, 2025
6729752
Only require macros at test time
balthasarschuess Sep 2, 2025
b07fd43
Add setup to bashrc
balthasarschuess Sep 19, 2025
cd3a8ea
Merge branch 'ros2-rust:main' into main
balthasarschuess Sep 19, 2025
e760adf
Fix bug in Dockerfile
balthasarschuess Sep 19, 2025
36de62a
Fix cargo fmt
balthasarschuess Sep 19, 2025
4665263
Follow standard macro package naming convention
balthasarschuess Sep 19, 2025
5f93232
Prepare macro to implement enum derive
balthasarschuess Sep 19, 2025
0879517
Reduce visibility of struct macro implementation
balthasarschuess Sep 19, 2025
74cf511
Extend macro with more builder options
balthasarschuess Sep 19, 2025
6616988
support discimator function
balthasarschuess Sep 19, 2025
e688869
Fix bug
balthasarschuess Sep 19, 2025
22a022a
Add support for ranges
balthasarschuess Sep 19, 2025
e13ed4b
Add tests for all macro options
balthasarschuess Sep 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"rclrs",
"rclrs_proc_macros",
]
resolver = "2"
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ RUN apt-get update && apt-get install -y \
libclang-dev \
tmux \
python3-pip \
ros-humble-test-msgs \
ros-humble-example-interfaces \
&& rm -rf /var/lib/apt/lists/*

# Install Rust
Expand All @@ -22,8 +24,10 @@ COPY src/ros2_rust/docker/rosidl_rust_setup.sh /
RUN ./rosidl_rust_setup.sh

RUN mkdir -p /workspace && echo "Did you forget to mount the repository into the Docker container?" > /workspace/HELLO.txt
RUN echo -e "\nsource /opt/ros/${ROS_DISTRO}/setup.sh"
WORKDIR /workspace


COPY src/ros2_rust/docker/rosidl_rust_entrypoint.sh /
ENTRYPOINT ["/rosidl_rust_entrypoint.sh"]
CMD ["/bin/bash"]
2 changes: 2 additions & 0 deletions rclrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ rosidl_runtime_rs = "0.4"
serde = { version = "1", optional = true, features = ["derive"] }
serde-big-array = { version = "0.5.1", optional = true }

rclrs_proc_macros = {path = "../rclrs_proc_macros"}

[dev-dependencies]
# Needed for e.g. writing yaml files in tests
tempfile = "3.3.0"
Expand Down
5 changes: 3 additions & 2 deletions rclrs/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
<depend>builtin_interfaces</depend>
<depend>rcl_interfaces</depend>
<depend>rosgraph_msgs</depend>
<depend>rosidl_default_generators</depend>

<test_depend>test_msgs</test_depend>
<test_depend>example_interfaces</test_depend>
<depend>test_msgs</depend>
<depend>example_interfaces</depend>

<export>
<build_type>ament_cargo</build_type>
Expand Down
2 changes: 2 additions & 0 deletions rclrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,5 @@ pub use time::*;
use time_source::*;
pub use wait_set::*;
pub use worker::*;

pub use rclrs_proc_macros::StructuredParameters;
94 changes: 94 additions & 0 deletions rclrs/src/parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,38 @@ impl ParameterInterface {
}
}

pub trait StructuredParameters: Sized {
fn declare_structured(
node: &crate::NodeState,
name: &str,
) -> core::result::Result<Self, crate::DeclarationError>;
}

impl<T: crate::ParameterVariant> StructuredParameters for crate::MandatoryParameter<T> {
fn declare_structured(
node: &crate::NodeState,
name: &str,
) -> core::result::Result<crate::MandatoryParameter<T>, crate::DeclarationError> {
node.declare_parameter(name).mandatory()
}
}
impl<T: crate::ParameterVariant> StructuredParameters for crate::OptionalParameter<T> {
fn declare_structured(
node: &crate::NodeState,
name: &str,
) -> core::result::Result<crate::OptionalParameter<T>, crate::DeclarationError> {
node.declare_parameter(name).optional()
}
}
impl<T: crate::ParameterVariant> StructuredParameters for crate::ReadOnlyParameter<T> {
fn declare_structured(
node: &crate::NodeState,
name: &str,
) -> core::result::Result<crate::ReadOnlyParameter<T>, crate::DeclarationError> {
node.declare_parameter(name).read_only()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -1410,4 +1442,66 @@ mod tests {
.optional()
.unwrap();
}

use crate as rclrs;
use rclrs_proc_macros::StructuredParameters;

#[derive(StructuredParameters, Debug)]
struct SimpleStructuredParameters {
_mandatory: MandatoryParameter<f64>,
_optional: OptionalParameter<f64>,
_readonly: ReadOnlyParameter<f64>,
}

#[test]
fn test_simple_structured_parameters() {
let args: Vec<String> = [
"test",
"--ros-args",
"-p",
"_mandatory:=1.0",
"-p",
"_optional:=1.0",
"-p",
"_readonly:=1.0",
]
.into_iter()
.map(str::to_string)
.collect();

let context = crate::Context::new(args, InitOptions::default()).unwrap();
let exec = context.create_basic_executor();
let node = exec.create_node(NodeOptions::new("test")).unwrap();
let _params = SimpleStructuredParameters::declare_structured(&node, "").unwrap();
}

#[derive(StructuredParameters, Debug)]
struct NestedStructuredParameters {
_simple: SimpleStructuredParameters,
_mandatory: MandatoryParameter<Arc<str>>,
}

#[test]
fn test_nested_structured_parameters() {
let args: Vec<String> = [
"test",
"--ros-args",
"-p",
"nested._simple._mandatory:=1.0",
"-p",
"nested._simple._optional:=1.0",
"-p",
"nested._simple._readonly:=1.0",
"-p",
"nested._mandatory:=foo",
]
.into_iter()
.map(str::to_string)
.collect();

let context = crate::Context::new(args, InitOptions::default()).unwrap();
let exec = context.create_basic_executor();
let node = exec.create_node(NodeOptions::new("test")).unwrap();
let _params = NestedStructuredParameters::declare_structured(&node, "nested").unwrap();
}
}
18 changes: 18 additions & 0 deletions rclrs_proc_macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name= "rclrs_proc_macros"
version = "0.0.1"
authors = ["Balthasar Schüss <[email protected]>"]
edition = "2021"
license = "Apache-2.0"
description = "A rust library providing proc macros for rclrs"
rust-version = "1.75"


[lib]
path = "src/lib.rs"
proc-macro = true

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = "2.0"
67 changes: 67 additions & 0 deletions rclrs_proc_macros/src/impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::DeriveInput;

pub(crate) fn derive_struct_parameters(input: DeriveInput) -> syn::Result<TokenStream> {
let ident = input.ident;

let fields = match input.data {
syn::Data::Struct(ref s) => &s.fields,
_ => {
return syn::Result::Err(syn::Error::new_spanned(
ident,
"StrucutredParameter trait can only be derived for structs",
));
}
};

let field_types: Vec<_> = fields.iter().map(|f| &f.ty).collect();

let mut args = Vec::new();
for f in fields {
let ident = f.ident.as_ref().unwrap();
let ident_str = syn::LitStr::new(&f.ident.as_ref().unwrap().to_string(), ident.span());
let field_type = match &f.ty {
syn::Type::Path(p) => {
let mut p = p.path.clone();
for segment in &mut p.segments {
segment.arguments = syn::PathArguments::None;
}
p
}
e => {
return syn::Result::Err(syn::Error::new_spanned(
e,
"attribute can only be path type",
));
}
};
let r = quote! {
#ident : #field_type::declare_structured(
node, &{match name {
"" => #ident_str.to_string(),
prefix => [prefix, ".", #ident_str].concat(),
}
})?,
};
args.push(r);
}

let result = quote!(
impl #ident {
const _ASSERT_PARAMETER: fn() = || {
fn assert_parameter<T: rclrs::StructuredParameters>() {}
#(
assert_parameter::<#field_types>();
)*
};
}

impl rclrs::StructuredParameters for #ident {
fn declare_structured(node: &rclrs::NodeState, name: &str) -> core::result::Result<Self, rclrs::DeclarationError> {
core::result::Result::Ok(Self{ #(#args)*})
}
}
);
syn::Result::Ok(result)
}
11 changes: 11 additions & 0 deletions rclrs_proc_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod r#impl;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(StructuredParameters)]
pub fn derive_struct_parameters(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
r#impl::derive_struct_parameters(input)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
Loading