Skip to content

Commit 3094ff8

Browse files
committed
Structured parameters initial draft
1 parent 5696315 commit 3094ff8

File tree

9 files changed

+202
-2
lines changed

9 files changed

+202
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[workspace]
22
members = [
33
"rclrs",
4+
"rclrs_proc_macros",
45
]
56
resolver = "2"

Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ RUN apt-get update && apt-get install -y \
99
libclang-dev \
1010
tmux \
1111
python3-pip \
12+
ros-humble-test-msgs \
13+
ros-humble-example-interfaces \
1214
&& rm -rf /var/lib/apt/lists/*
1315

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

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

30+
2731
COPY src/ros2_rust/docker/rosidl_rust_entrypoint.sh /
2832
ENTRYPOINT ["/rosidl_rust_entrypoint.sh"]
2933
CMD ["/bin/bash"]

rclrs/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ rosidl_runtime_rs = "0.4"
3636
serde = { version = "1", optional = true, features = ["derive"] }
3737
serde-big-array = { version = "0.5.1", optional = true }
3838

39+
rclrs_proc_macros = {path = "../rclrs_proc_macros"}
40+
3941
[dev-dependencies]
4042
# Needed for e.g. writing yaml files in tests
4143
tempfile = "3.3.0"

rclrs/package.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
<depend>builtin_interfaces</depend>
2020
<depend>rcl_interfaces</depend>
2121
<depend>rosgraph_msgs</depend>
22+
<depend>rosidl_default_generators</depend>
2223

23-
<test_depend>test_msgs</test_depend>
24-
<test_depend>example_interfaces</test_depend>
24+
<depend>test_msgs</depend>
25+
<depend>example_interfaces</depend>
2526

2627
<export>
2728
<build_type>ament_cargo</build_type>

rclrs/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,5 @@ pub use time::*;
223223
use time_source::*;
224224
pub use wait_set::*;
225225
pub use worker::*;
226+
227+
pub use rclrs_proc_macros::StructuredParameters;

rclrs/src/parameter.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,38 @@ impl ParameterInterface {
871871
}
872872
}
873873

874+
pub trait StructuredParameters: Sized {
875+
fn declare_structured(
876+
node: &crate::NodeState,
877+
name: &str,
878+
) -> core::result::Result<Self, crate::DeclarationError>;
879+
}
880+
881+
impl<T: crate::ParameterVariant> StructuredParameters for crate::MandatoryParameter<T> {
882+
fn declare_structured(
883+
node: &crate::NodeState,
884+
name: &str,
885+
) -> core::result::Result<crate::MandatoryParameter<T>, crate::DeclarationError> {
886+
node.declare_parameter(name).mandatory()
887+
}
888+
}
889+
impl<T: crate::ParameterVariant> StructuredParameters for crate::OptionalParameter<T> {
890+
fn declare_structured(
891+
node: &crate::NodeState,
892+
name: &str,
893+
) -> core::result::Result<crate::OptionalParameter<T>, crate::DeclarationError> {
894+
node.declare_parameter(name).optional()
895+
}
896+
}
897+
impl<T: crate::ParameterVariant> StructuredParameters for crate::ReadOnlyParameter<T> {
898+
fn declare_structured(
899+
node: &crate::NodeState,
900+
name: &str,
901+
) -> core::result::Result<crate::ReadOnlyParameter<T>, crate::DeclarationError> {
902+
node.declare_parameter(name).read_only()
903+
}
904+
}
905+
874906
#[cfg(test)]
875907
mod tests {
876908
use super::*;
@@ -1410,4 +1442,66 @@ mod tests {
14101442
.optional()
14111443
.unwrap();
14121444
}
1445+
1446+
use crate as rclrs;
1447+
use rclrs_proc_macros::StructuredParameters;
1448+
1449+
#[derive(StructuredParameters, Debug)]
1450+
struct SimpleStructuredParameters {
1451+
_mandatory: MandatoryParameter<f64>,
1452+
_optional: OptionalParameter<f64>,
1453+
_readonly: ReadOnlyParameter<f64>,
1454+
}
1455+
1456+
#[test]
1457+
fn test_simple_structured_parameters() {
1458+
let args: Vec<String> = [
1459+
"test",
1460+
"--ros-args",
1461+
"-p",
1462+
"_mandatory:=1.0",
1463+
"-p",
1464+
"_optional:=1.0",
1465+
"-p",
1466+
"_readonly:=1.0",
1467+
]
1468+
.into_iter()
1469+
.map(str::to_string)
1470+
.collect();
1471+
1472+
let context = crate::Context::new(args, InitOptions::default()).unwrap();
1473+
let exec = context.create_basic_executor();
1474+
let node = exec.create_node(NodeOptions::new("test")).unwrap();
1475+
let _params = SimpleStructuredParameters::declare_structured(&node, "").unwrap();
1476+
}
1477+
1478+
#[derive(StructuredParameters, Debug)]
1479+
struct NestedStructuredParameters {
1480+
_simple: SimpleStructuredParameters,
1481+
_mandatory: MandatoryParameter<Arc<str>>,
1482+
}
1483+
1484+
#[test]
1485+
fn test_nested_structured_parameters() {
1486+
let args: Vec<String> = [
1487+
"test",
1488+
"--ros-args",
1489+
"-p",
1490+
"nested._simple._mandatory:=1.0",
1491+
"-p",
1492+
"nested._simple._optional:=1.0",
1493+
"-p",
1494+
"nested._simple._readonly:=1.0",
1495+
"-p",
1496+
"nested._mandatory:=foo",
1497+
]
1498+
.into_iter()
1499+
.map(str::to_string)
1500+
.collect();
1501+
1502+
let context = crate::Context::new(args, InitOptions::default()).unwrap();
1503+
let exec = context.create_basic_executor();
1504+
let node = exec.create_node(NodeOptions::new("test")).unwrap();
1505+
let _params = NestedStructuredParameters::declare_structured(&node, "nested").unwrap();
1506+
}
14131507
}

rclrs_proc_macros/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name= "rclrs_proc_macros"
3+
version = "0.0.1"
4+
authors = ["Balthasar Schüss <[email protected]>"]
5+
edition = "2021"
6+
license = "Apache-2.0"
7+
description = "A rust library providing proc macros for rclrs"
8+
rust-version = "1.75"
9+
10+
11+
[lib]
12+
path = "src/lib.rs"
13+
proc-macro = true
14+
15+
[dependencies]
16+
proc-macro2 = "1.0"
17+
quote = "1.0"
18+
syn = "2.0"

rclrs_proc_macros/src/impl.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use proc_macro2::TokenStream;
2+
use quote::quote;
3+
use syn::DeriveInput;
4+
5+
pub(crate) fn derive_struct_parameters(input: DeriveInput) -> syn::Result<TokenStream> {
6+
let ident = input.ident;
7+
8+
let fields = match input.data {
9+
syn::Data::Struct(ref s) => &s.fields,
10+
_ => {
11+
return syn::Result::Err(syn::Error::new_spanned(
12+
ident,
13+
"StrucutredParameter trait can only be derived for structs",
14+
));
15+
}
16+
};
17+
18+
let field_types: Vec<_> = fields.iter().map(|f| &f.ty).collect();
19+
20+
let mut args = Vec::new();
21+
for f in fields {
22+
let ident = f.ident.as_ref().unwrap();
23+
let ident_str = syn::LitStr::new(&f.ident.as_ref().unwrap().to_string(), ident.span());
24+
let field_type = match &f.ty {
25+
syn::Type::Path(p) => {
26+
let mut p = p.path.clone();
27+
for segment in &mut p.segments {
28+
segment.arguments = syn::PathArguments::None;
29+
}
30+
p
31+
}
32+
e => {
33+
return syn::Result::Err(syn::Error::new_spanned(
34+
e,
35+
"attribute can only be path type",
36+
));
37+
}
38+
};
39+
let r = quote! {
40+
#ident : #field_type::declare_structured(
41+
node, &{match name {
42+
"" => #ident_str.to_string(),
43+
prefix => [prefix, ".", #ident_str].concat(),
44+
}
45+
})?,
46+
};
47+
args.push(r);
48+
}
49+
50+
let result = quote!(
51+
impl #ident {
52+
const _ASSERT_PARAMETER: fn() = || {
53+
fn assert_parameter<T: rclrs::StructuredParameters>() {}
54+
#(
55+
assert_parameter::<#field_types>();
56+
)*
57+
};
58+
}
59+
60+
impl rclrs::StructuredParameters for #ident {
61+
fn declare_structured(node: &rclrs::NodeState, name: &str) -> core::result::Result<Self, rclrs::DeclarationError> {
62+
core::result::Result::Ok(Self{ #(#args)*})
63+
}
64+
}
65+
);
66+
syn::Result::Ok(result)
67+
}

rclrs_proc_macros/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
mod r#impl;
2+
use proc_macro::TokenStream;
3+
use syn::{parse_macro_input, DeriveInput};
4+
5+
#[proc_macro_derive(StructuredParameters)]
6+
pub fn derive_struct_parameters(input: TokenStream) -> TokenStream {
7+
let input = parse_macro_input!(input as DeriveInput);
8+
r#impl::derive_struct_parameters(input)
9+
.unwrap_or_else(|e| e.to_compile_error())
10+
.into()
11+
}

0 commit comments

Comments
 (0)