Skip to main content

webots_proto_schema/
macros.rs

1/// Defines a struct representing a generic Webots node.
2#[macro_export]
3macro_rules! define_node {
4    (
5        $(#[$meta:meta])*
6        $Name:ident $(($WebotsName:literal))? {
7            $(
8                $(#[$field_meta:meta])*
9                $field:ident : $Type:ty
10            ),* $(,)?
11        }
12    ) => {
13        $(#[$meta])*
14        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, new, Setters)]
15        #[serde(rename_all = "camelCase")]
16        $(
17            #[serde(rename = $WebotsName)]
18        )?
19        #[setters(prefix = "with_", strip_option, into)]
20        pub struct $Name {
21            $(
22                $(#[$field_meta])*
23                #[serde(default, skip_serializing_if = "Option::is_none")]
24                #[new(default)]
25                pub $field: Option<$crate::types::ProtoField<$Type>>,
26            )*
27        }
28
29        impl $crate::proto::schema::WebotsNode for $Name {
30            fn node_name() -> &'static str {
31                #[allow(unused_variables)]
32                let name = stringify!($Name);
33                $(
34                    let name = $WebotsName;
35                )?
36                name
37            }
38
39            fn all_fields() -> &'static [(&'static str, $crate::proto::ast::FieldType)] {
40                const FIELDS: &[(&str, $crate::proto::ast::FieldType)] = &[
41                    $(
42                        (stringify!($field), $crate::map_field_type!($Type)),
43                    )*
44                ];
45                FIELDS
46            }
47        }
48    };
49}
50
51/// Maps a Rust type to a Webots `FieldType`.
52#[macro_export]
53macro_rules! map_field_type {
54    ($Type:ty) => {
55        <$Type as $crate::proto::schema::WebotsFieldType>::FIELD_TYPE
56    };
57}
58
59/// Defines a struct representing a Webots `Device` node.
60/// Includes standard `Device` fields (`name`, `model`, `description`).
61#[macro_export]
62macro_rules! define_device {
63    (
64        $(#[$meta:meta])*
65        $Name:ident {
66            $(
67                $(#[$field_meta:meta])*
68                $field:ident : $Type:ty
69            ),* $(,)?
70        }
71    ) => {
72        $(#[$meta])*
73        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, new, Setters)]
74        #[serde(rename_all = "camelCase")]
75        #[setters(prefix = "with_", strip_option, into)]
76        pub struct $Name {
77            // --- Device Fields ---
78            #[new(into)]
79            #[setters(skip)]
80            pub name: $crate::types::ProtoField<$crate::types::SFString>,
81            /// Default: ""
82            #[serde(default, skip_serializing_if = "Option::is_none")]
83            #[new(default)]
84            pub model: Option<$crate::types::ProtoField<$crate::types::SFString>>,
85            /// Default: ""
86            #[serde(default, skip_serializing_if = "Option::is_none")]
87            #[new(default)]
88            pub description: Option<$crate::types::ProtoField<$crate::types::SFString>>,
89
90            // --- User Fields ---
91            $(
92                $(#[$field_meta])*
93                #[serde(default, skip_serializing_if = "Option::is_none")]
94                #[new(default)]
95                pub $field: Option<$crate::types::ProtoField<$Type>>,
96            )*
97        }
98
99        impl $crate::proto::schema::WebotsNode for $Name {
100            fn node_name() -> &'static str {
101                stringify!($Name)
102            }
103
104            fn all_fields() -> &'static [(&'static str, $crate::proto::ast::FieldType)] {
105                const FIELDS: &[(&str, $crate::proto::ast::FieldType)] = &[
106                    ("name", $crate::proto::ast::FieldType::SFString),
107                    ("model", $crate::proto::ast::FieldType::SFString),
108                    ("description", $crate::proto::ast::FieldType::SFString),
109                    $(
110                        (stringify!($field), $crate::map_field_type!($Type)),
111                    )*
112                ];
113                FIELDS
114            }
115        }
116    };
117}
118
119/// Defines a struct representing a Webots `Solid` node.
120/// Includes standard `Solid` fields (pose, physics, boundingObject) AND `Device` fields.
121#[macro_export]
122macro_rules! define_solid {
123    (
124        $(#[$meta:meta])*
125        $Name:ident {
126            $(
127                $(#[$field_meta:meta])*
128                $field:ident : $Type:ty
129            ),* $(,)?
130        }
131    ) => {
132        $(#[$meta])*
133        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, new, Setters)]
134        #[serde(rename_all = "camelCase")]
135        #[setters(prefix = "with_", strip_option, into)]
136        pub struct $Name {
137            // --- Pose Fields ---
138            /// Default: 0 0 0
139            #[serde(default, skip_serializing_if = "Option::is_none")]
140            #[new(default)]
141            pub translation: Option<$crate::types::ProtoField<$crate::types::SFVec3f>>,
142            /// Default: 0 0 1 0
143            #[serde(default, skip_serializing_if = "Option::is_none")]
144            #[new(default)]
145            pub rotation: Option<$crate::types::ProtoField<$crate::types::SFRotation>>,
146            /// Default: []
147            #[serde(default, skip_serializing_if = "Option::is_none")]
148            #[new(default)]
149            pub children: Option<$crate::types::ProtoField<MFNode>>,
150
151            // --- Device Fields ---
152            #[new(into)]
153            #[setters(skip)]
154            pub name: $crate::types::ProtoField<$crate::types::SFString>,
155            /// Default: ""
156            #[serde(default, skip_serializing_if = "Option::is_none")]
157            #[new(default)]
158            pub model: Option<$crate::types::ProtoField<$crate::types::SFString>>,
159            /// Default: ""
160            #[serde(default, skip_serializing_if = "Option::is_none")]
161            #[new(default)]
162            pub description: Option<$crate::types::ProtoField<$crate::types::SFString>>,
163
164            // --- Solid Fields ---
165            /// Default: "default"
166            #[serde(default, skip_serializing_if = "Option::is_none")]
167            #[new(default)]
168            pub contact_material: Option<$crate::types::ProtoField<$crate::types::SFString>>,
169            /// Default: []
170            #[serde(default, skip_serializing_if = "Option::is_none")]
171            #[new(default)]
172            pub immersion_properties: Option<$crate::types::ProtoField<MFNode>>,
173            /// Default: NULL
174            #[serde(default, skip_serializing_if = "Option::is_none")]
175            #[new(default)]
176            pub bounding_object: Option<$crate::types::ProtoField<Box<Node>>>,
177            /// Default: NULL
178            #[serde(default, skip_serializing_if = "Option::is_none")]
179            #[new(default)]
180            pub physics: Option<$crate::types::ProtoField<Box<Node>>>,
181            /// Default: FALSE
182            #[serde(default, skip_serializing_if = "Option::is_none")]
183            #[new(default)]
184            pub locked: Option<$crate::types::ProtoField<$crate::types::SFBool>>,
185            /// Default: 0.0
186            #[serde(default, skip_serializing_if = "Option::is_none")]
187            #[new(default)]
188            pub radar_cross_section: Option<$crate::types::ProtoField<$crate::types::SFFloat>>,
189            /// Default: []
190            #[serde(default, skip_serializing_if = "Option::is_none")]
191            #[new(default)]
192            pub recognition_colors: Option<$crate::types::ProtoField<$crate::types::MFColor>>,
193
194            // --- Hidden Solid fields ---
195            /// Default: 0 0 0
196            #[serde(default, skip_serializing_if = "Option::is_none")]
197            #[new(default)]
198            pub linear_velocity: Option<$crate::types::ProtoField<$crate::types::SFVec3f>>,
199            /// Default: 0 0 0
200            #[serde(default, skip_serializing_if = "Option::is_none")]
201            #[new(default)]
202            pub angular_velocity: Option<$crate::types::ProtoField<$crate::types::SFVec3f>>,
203
204            // --- User Fields ---
205            $(
206                $(#[$field_meta])*
207                #[serde(default, skip_serializing_if = "Option::is_none")]
208                #[new(default)]
209                pub $field: Option<$crate::types::ProtoField<$Type>>,
210            )*
211        }
212
213        impl $crate::proto::schema::WebotsNode for $Name {
214            fn node_name() -> &'static str {
215                stringify!($Name)
216            }
217
218            fn all_fields() -> &'static [(&'static str, $crate::proto::ast::FieldType)] {
219                const FIELDS: &[(&str, $crate::proto::ast::FieldType)] = &[
220                    ("translation", $crate::proto::ast::FieldType::SFVec3f),
221                    ("rotation", $crate::proto::ast::FieldType::SFRotation),
222                    ("children", $crate::proto::ast::FieldType::MFNode),
223                    ("name", $crate::proto::ast::FieldType::SFString),
224                    ("model", $crate::proto::ast::FieldType::SFString),
225                    ("description", $crate::proto::ast::FieldType::SFString),
226                    ("contactMaterial", $crate::proto::ast::FieldType::SFString),
227                    ("immersionProperties", $crate::proto::ast::FieldType::MFNode),
228                    ("boundingObject", $crate::proto::ast::FieldType::SFNode),
229                    ("physics", $crate::proto::ast::FieldType::SFNode),
230                    ("locked", $crate::proto::ast::FieldType::SFBool),
231                    ("radarCrossSection", $crate::proto::ast::FieldType::SFFloat),
232                    ("recognitionColors", $crate::proto::ast::FieldType::MFColor),
233                    ("linearVelocity", $crate::proto::ast::FieldType::SFVec3f),
234                    ("angularVelocity", $crate::proto::ast::FieldType::SFVec3f),
235                    $(
236                        (stringify!($field), $crate::map_field_type!($Type)),
237                    )*
238                ];
239                FIELDS
240            }
241        }
242    };
243}
244
245/// Defines a struct representing a Webots `Motor` node (or descendent).
246/// Includes `Device` fields and standard `Motor` fields.
247#[macro_export]
248macro_rules! define_motor {
249    (
250        $(#[$meta:meta])*
251        $Name:ident {
252            $(
253                $(#[$field_meta:meta])*
254                $field:ident : $Type:ty
255            ),* $(,)?
256        }
257    ) => {
258        $(#[$meta])*
259        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, new, Setters, Default)]
260        #[serde(rename_all = "camelCase")]
261        #[setters(prefix = "with_", strip_option, into)]
262        pub struct $Name {
263            // --- Device Fields ---
264            #[new(into)]
265            #[setters(skip)]
266            pub name: $crate::types::ProtoField<$crate::types::SFString>,
267            /// Default: ""
268            #[serde(default, skip_serializing_if = "Option::is_none")]
269            #[new(default)]
270            pub model: Option<$crate::types::ProtoField<$crate::types::SFString>>,
271            /// Default: ""
272            #[serde(default, skip_serializing_if = "Option::is_none")]
273            #[new(default)]
274            pub description: Option<$crate::types::ProtoField<$crate::types::SFString>>,
275
276            // --- Motor Fields ---
277            /// Default: -1
278            #[serde(default, skip_serializing_if = "Option::is_none")]
279            #[new(default)]
280            pub acceleration: Option<$crate::types::ProtoField<$crate::types::SFFloat>>,
281            /// Default: 10
282            #[serde(default, skip_serializing_if = "Option::is_none")]
283            #[new(default)]
284            pub consumption_factor: Option<$crate::types::ProtoField<$crate::types::SFFloat>>,
285            /// Default: 10 0 0
286            #[serde(rename = "controlPID", default, skip_serializing_if = "Option::is_none")]
287            #[new(default)]
288            pub control_pid: Option<$crate::types::ProtoField<$crate::types::SFVec3f>>,
289            /// Default: 0
290            #[serde(default, skip_serializing_if = "Option::is_none")]
291            #[new(default)]
292            pub min_position: Option<$crate::types::ProtoField<$crate::types::SFFloat>>,
293            /// Default: 10
294            #[serde(default, skip_serializing_if = "Option::is_none")]
295            #[new(default)]
296            pub max_position: Option<$crate::types::ProtoField<$crate::types::SFFloat>>,
297            /// Default: 10
298            #[serde(default, skip_serializing_if = "Option::is_none")]
299            #[new(default)]
300            pub max_velocity: Option<$crate::types::ProtoField<$crate::types::SFFloat>>,
301            /// Default: 1
302            #[serde(default, skip_serializing_if = "Option::is_none")]
303            #[new(default)]
304            pub multiplier: Option<$crate::types::ProtoField<$crate::types::SFFloat>>,
305            /// Default: "sound_url"
306            #[serde(default, skip_serializing_if = "Option::is_none")]
307            #[new(default)]
308            pub sound: Option<$crate::types::ProtoField<$crate::types::SFString>>,
309            /// Default: []
310            #[serde(default, skip_serializing_if = "Option::is_none")]
311            #[new(default)]
312            pub muscles: Option<$crate::types::ProtoField<MFNode>>,
313
314            // --- User Fields ---
315            $(
316                $(#[$field_meta])*
317                #[serde(default, skip_serializing_if = "Option::is_none")]
318                #[new(default)]
319                pub $field: Option<$crate::types::ProtoField<$Type>>,
320            )*
321        }
322
323        impl $crate::proto::schema::WebotsNode for $Name {
324            fn node_name() -> &'static str {
325                stringify!($Name)
326            }
327
328            fn all_fields() -> &'static [(&'static str, $crate::proto::ast::FieldType)] {
329                const FIELDS: &[(&str, $crate::proto::ast::FieldType)] = &[
330                    ("name", $crate::proto::ast::FieldType::SFString),
331                    ("model", $crate::proto::ast::FieldType::SFString),
332                    ("description", $crate::proto::ast::FieldType::SFString),
333                    ("acceleration", $crate::proto::ast::FieldType::SFFloat),
334                    ("consumptionFactor", $crate::proto::ast::FieldType::SFFloat),
335                    ("controlPID", $crate::proto::ast::FieldType::SFVec3f),
336                    ("minPosition", $crate::proto::ast::FieldType::SFFloat),
337                    ("maxPosition", $crate::proto::ast::FieldType::SFFloat),
338                    ("maxVelocity", $crate::proto::ast::FieldType::SFFloat),
339                    ("multiplier", $crate::proto::ast::FieldType::SFFloat),
340                    ("sound", $crate::proto::ast::FieldType::SFString),
341                    ("muscles", $crate::proto::ast::FieldType::MFNode),
342                    $(
343                        (stringify!($field), $crate::map_field_type!($Type)),
344                    )*
345                ];
346                FIELDS
347            }
348        }
349    };
350}