Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 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
67ff819
Merge branch 'main' into main
balthasarschuess Dec 17, 2025
b1f30bf
Improve documentation
balthasarschuess Jan 12, 2026
cdc137a
Merge remote-tracking branch 'ros2_rust/main'
balthasarschuess Feb 17, 2026
a9c91f9
Patch path for windows build
balthasarschuess Feb 17, 2026
a90a1bf
Revert "Patch path for windows build"
balthasarschuess Feb 18, 2026
d8becb9
Bump compiler version for rclrs-macros
balthasarschuess Feb 18, 2026
30b1be8
Make rclrs-macros buildable as ros package on windows
balthasarschuess Feb 18, 2026
3e084c7
Remove rclrs-macros as ros dependency from rclrs
balthasarschuess Feb 18, 2026
69cf835
Fix email
balthasarschuess Feb 18, 2026
8736c66
Remove changes to dockerfile
balthasarschuess Feb 18, 2026
32d65d9
Revert "Remove rclrs-macros as ros dependency from rclrs"
balthasarschuess Feb 18, 2026
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-macros",
]
resolver = "2"
18 changes: 18 additions & 0 deletions rclrs-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name= "rclrs-macros"
version = "0.0.1"
authors = ["Balthasar Schüss <rclrs@bschuess.dev>"]
edition = "2021"
license = "Apache-2.0"
description = "A rust library providing proc macros for rclrs"
rust-version = "1.85"


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

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = "2.0"
18 changes: 18 additions & 0 deletions rclrs-macros/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<?xml-model
href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>rclrs-macros</name>
<version>0.5.0</version>
<description>Package containing procedural macros for the Rust client library.</description>
<maintainer email="esteve@apache.org">Esteve Fernandez</maintainer>
<maintainer email="nnmmgit@gmail.com">Nikolai Morin</maintainer>
<!-- This project is not military-sponsored, Jacob's employment contract just requires him to use this email address -->
<maintainer email="jacob.a.hassold.civ@army.mil">Jacob Hassold</maintainer>
<license>Apache License 2.0</license>
<author email="rclrs@bschuess.dev">Balthasar Schüss</author>
<export>
<build_type>ament_cargo</build_type>
</export>
</package>
162 changes: 162 additions & 0 deletions rclrs-macros/src/impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Expr};

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

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

fn derive_structured_parameters_struct(
ident: proc_macro2::Ident,
struct_: &syn::DataStruct,
) -> syn::Result<TokenStream> {
let fields = &struct_.fields;

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 mut default: Option<Expr> = None;
let mut description: Option<Expr> = None;
let mut constraints: Option<Expr> = None;
let mut ignore_override = false;
let mut discard_mismatching_prior_value = false;
let mut discriminate: Option<Expr> = None;
let mut range: Option<Expr> = None;

for attr in &f.attrs {
if attr.path().is_ident("param") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("default") {
default = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("description") {
description = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("constraints") {
constraints = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("ignore_override") {
ignore_override = true;
Ok(())
} else if meta.path.is_ident("discard_mismatching_prior_value") {
discard_mismatching_prior_value = true;
Ok(())
} else if meta.path.is_ident("discriminate") {
discriminate = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("range") {
range = Some(meta.value()?.parse()?);
Ok(())
} else {
let err = format!("Unknown key: {:?}", &meta.path.get_ident());
syn::Result::Err(syn::Error::new_spanned(meta.path, err))
}
})?;
}
}

let default = match default {
Some(expr) => quote! {Some(#expr)},
None => quote! {None},
};
let description = match description {
Some(expr) => quote! {#expr},
None => quote! {""},
};
let constraints = match constraints {
Some(expr) => quote! {#expr},
None => quote! {""},
};
let discriminate = match discriminate {
Some(expr) => quote! {
core::option::Option::Some(Box::new(#expr))
},
None => quote! {core::option::Option::None},
};
let range = match range {
Some(expr) => quote! {
core::option::Option::Some(#expr)
},
None => quote! {core::option::Option::None},
};

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,
"Only PathType attributes are supported",
));
}
};
let r = quote! {
#ident : #field_type::declare_structured(
node,
&{match name {
"" => #ident_str.to_string(),
prefix => [prefix, ".", #ident_str].concat(),
}},
#default,
#description,
#constraints,
#ignore_override,
#discard_mismatching_prior_value,
#discriminate,
#range,
)?,
};
args.push(r);
}

let result = quote!(
impl #ident {
const _ASSERT_PARAMETER: fn() = || {
fn assert_parameter<T: rclrs::parameter::structured::StructuredParameters>() {}
#(
assert_parameter::<#field_types>();
)*
};
}
impl rclrs::parameter::structured::StructuredParametersMeta<rclrs::parameter::structured::DefaultForbidden> for #ident {
fn declare_structured_(
node: &rclrs::NodeState,
name: &str,
default: core::option::Option<rclrs::parameter::structured::DefaultForbidden>,
description: impl core::convert::Into<std::sync::Arc<str>>,
constraints: impl core::convert::Into<std::sync::Arc<str>>,
ignore_override: bool,
discard_mismatching_prior_value: bool,
discriminate: core::option::Option<Box<
dyn core::ops::FnOnce(rclrs::AvailableValues<rclrs::parameter::structured::DefaultForbidden>)
-> core::option::Option<rclrs::parameter::structured::DefaultForbidden
>>>,
range: core::option::Option<<rclrs::structured::DefaultForbidden as rclrs::ParameterVariant>::Range>,
)
-> core::result::Result<Self, rclrs::DeclarationError> {
core::result::Result::Ok(Self{ #(#args)*})
}

}
impl rclrs::StructuredParameters for #ident {}
);
syn::Result::Ok(result)
}
11 changes: 11 additions & 0 deletions rclrs-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, attributes(param))]
pub fn derive_structured_parameters(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
r#impl::derive_structured_parameters(input)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
1 change: 1 addition & 0 deletions rclrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ uuid = { version = "1", features = ["v4"] }
tempfile = "3.3.0"
# Needed for parameter service tests
tokio = { version = "1", features = ["rt", "time", "macros"] }
rclrs-macros = {path = "../rclrs-macros"}

[build-dependencies]
# Needed for uploading documentation to docs.rs
Expand Down
1 change: 1 addition & 0 deletions rclrs/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<depend>builtin_interfaces</depend>
<depend>rcl_interfaces</depend>
<depend>rosgraph_msgs</depend>
<depend>rclrs-macros</depend>

<test_depend>test_msgs</test_depend>
<test_depend>example_interfaces</test_depend>
Expand Down
5 changes: 4 additions & 1 deletion rclrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,10 @@ pub use error::*;
pub use executor::*;
pub use logging::*;
pub use node::*;
pub use parameter::*;
pub use parameter::{
structured::{StructuredParameters, StructuredParametersMeta},
*,
};
pub use publisher::*;
pub use qos::*;
pub use rcl_bindings::rmw_request_id_t;
Expand Down
1 change: 1 addition & 0 deletions rclrs/src/parameter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod override_map;
mod range;
mod service;
pub mod structured;
mod value;

pub(crate) use override_map::*;
Expand Down
Loading