1use std::{marker::PhantomData, ops::{Deref, DerefMut}};
3use std::sync::{Mutex, MutexGuard};
4use std::ffi::c_char;
5
6use crate::mujoco_c::{mj_version, mjVERSION_HEADER};
7
8pub(crate) const ERROR_BUF_LEN: usize = 100;
12
13pub(crate) fn write_ascii_to_buf(buf: &mut [c_char], value: &str) {
20 assert!(value.is_ascii(), "value must be valid ASCII");
21 let c_string = std::ffi::CString::new(value).unwrap();
22 let bytes = c_string.into_bytes_with_nul();
23 let dest: &mut [u8] = bytemuck::cast_slice_mut(buf);
24 dest[..bytes.len()].copy_from_slice(&bytes);
25 dest[bytes.len()..].fill(0);
26}
27
28pub(crate) fn optional_sparse_addr_range<T>(addr_array: &[T], id: usize, data_len: usize) -> Option<(usize, usize)>
50where
51 T: Into<i64> + Copy,
52{
53 let adr: i64 = addr_array[id].into();
54 if adr < 0 {
55 return None;
56 }
57 let adr = adr as usize;
58 let len = addr_array
59 .get(id + 1..)
60 .unwrap_or(&[])
61 .iter()
62 .find(|&&next| next.into() != -1)
63 .map(|&next| next.into() as usize)
64 .unwrap_or(data_len)
65 - adr;
66 Some((adr, len))
67}
68
69
70#[doc(hidden)]
79#[macro_export]
80macro_rules! set_flag {
81 ($flags:expr, $mask:expr, $enabled:expr) => {
82 if $enabled {
83 $flags |= $mask;
84 } else {
85 $flags &= !$mask;
86 }
87 };
88}
89
90
91#[macro_export]
93#[doc(hidden)]
94macro_rules! mj_model_dyn_range {
95 ($model:expr, $id:expr, nq) => {
96 $crate::util::optional_sparse_addr_range($model.jnt_qposadr(), $id, $model.nq() as usize).unwrap_or((0, 0))
97 };
98 ($model:expr, $id:expr, nv) => {
99 $crate::util::optional_sparse_addr_range($model.jnt_dofadr(), $id, $model.nv() as usize).unwrap_or((0, 0))
100 };
101 ($model:expr, $id:expr, nsensordata) => {
102 $crate::util::optional_sparse_addr_range($model.sensor_adr(), $id, $model.nsensordata() as usize).unwrap_or((0, 0))
103 };
104 ($model:expr, $id:expr, ntupledata) => {
105 $crate::util::optional_sparse_addr_range($model.tuple_adr(), $id, $model.ntupledata() as usize).unwrap_or((0, 0))
106 };
107 ($model:expr, $id:expr, ntexdata) => {
108 $crate::util::optional_sparse_addr_range($model.tex_adr(), $id, $model.ntexdata() as usize).unwrap_or((0, 0))
109 };
110 ($model:expr, $id:expr, nnumericdata) => {
111 $crate::util::optional_sparse_addr_range($model.numeric_adr(), $id, $model.nnumericdata() as usize).unwrap_or((0, 0))
112 };
113 ($model:expr, $id:expr, nhfielddata) => {
114 $crate::util::optional_sparse_addr_range($model.hfield_adr(), $id, $model.nhfielddata() as usize).unwrap_or((0, 0))
115 };
116 ($model:expr, $id:expr, na) => {
117 $crate::util::optional_sparse_addr_range($model.actuator_actadr(), $id, $model.na() as usize).unwrap_or((0, 0))
118 };
119 ($model:expr, $id:expr, nJten) => {
120 $crate::util::optional_sparse_addr_range($model.ten_j_rowadr(), $id, $model.n_jten() as usize).unwrap_or((0, 0))
121 };
122}
123
124#[derive(Debug)]
132pub struct PointerViewMut<'d, T> {
133 ptr: *mut T,
134 len: usize,
135 phantom: PhantomData<&'d mut ()>
136}
137
138impl<'d, T> PointerViewMut<'d, T> {
139 pub(crate) const fn new(ptr: *mut T, len: usize, phantom: PhantomData<&'d mut ()>) -> Self {
140 Self {ptr, len, phantom}
141 }
142}
143
144impl<T> PartialEq for PointerViewMut<'_, T> {
146 fn eq(&self, other: &Self) -> bool {
147 self.ptr == other.ptr && self.len == other.len
148 }
149}
150
151impl<T> Eq for PointerViewMut<'_, T> {}
152
153impl<T> Deref for PointerViewMut<'_, T> {
154 type Target = [T];
155 fn deref(&self) -> &Self::Target {
156 if self.ptr.is_null() {
157 return &[];
158 }
159 unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
162 }
163}
164
165impl<T> DerefMut for PointerViewMut<'_, T> {
166 fn deref_mut(&mut self) -> &mut Self::Target {
167 if self.ptr.is_null() {
168 return &mut [];
169 }
170 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
173 }
174}
175
176#[derive(Debug)]
184pub struct PointerViewUnsafeMut<'d, T> {
185 ptr: *mut T,
186 len: usize,
187 phantom: PhantomData<&'d mut ()>
188}
189
190impl<'d, T> PointerViewUnsafeMut<'d, T> {
191 pub(crate) const fn new(ptr: *mut T, len: usize, phantom: PhantomData<&'d mut ()>) -> Self {
192 Self { ptr, len, phantom }
193 }
194
195 pub unsafe fn as_mut_slice(&mut self) -> &mut [T] {
203 if self.ptr.is_null() {
204 return &mut [];
205 }
206 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
209 }
210}
211
212impl<T> PartialEq for PointerViewUnsafeMut<'_, T> {
214 fn eq(&self, other: &Self) -> bool {
215 self.ptr == other.ptr && self.len == other.len
216 }
217}
218
219impl<T> Eq for PointerViewUnsafeMut<'_, T> {}
220
221impl<T> Deref for PointerViewUnsafeMut<'_, T> {
222 type Target = [T];
223 fn deref(&self) -> &Self::Target {
224 if self.ptr.is_null() {
225 return &[];
226 }
227 unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
230 }
231}
232
233#[derive(Debug)]
241pub struct PointerView<'d, T> {
242 ptr: *const T,
243 len: usize,
244 phantom: PhantomData<&'d ()>
245}
246
247impl<'d, T> PointerView<'d, T> {
248 pub(crate) const fn new(ptr: *const T, len: usize, phantom: PhantomData<&'d ()>) -> Self {
249 Self {ptr, len, phantom}
250 }
251}
252
253impl<T> PartialEq for PointerView<'_, T> {
255 fn eq(&self, other: &Self) -> bool {
256 self.ptr == other.ptr && self.len == other.len
257 }
258}
259
260impl<T> Eq for PointerView<'_, T> {}
261
262impl<T> Deref for PointerView<'_, T> {
263 type Target = [T];
264 fn deref(&self) -> &Self::Target {
265 if self.ptr.is_null() {
266 return &[];
267 }
268 unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
271 }
272}
273
274#[macro_export]
280#[doc(hidden)]
281macro_rules! eval_or_expand {
282 (@eval $(true)? { $($data:tt)* } ) => { $($data)* };
283 (@eval false { $($data:tt)* } ) => {};
284}
285
286#[macro_export]
299#[doc(hidden)]
300macro_rules! view_creator {
301 (
302 $self:expr, $view:ident, $data:expr,
303 [$($([$prefix_field:ident])? $field:ident : $type_:ty $([$force:ident])?),*],
304 [$($([$prefix_field_ro:ident])? $field_ro:ident : $type_ro:ty $([$force_ro:ident])?),*],
305 [$($([$prefix_opt_field:ident])? $opt_field:ident : $type_opt:ty $([$force_opt:ident])?),*],
306 $ptr_view:expr,
307 $ptr_view_ro:expr
308 ) => {
309 paste::paste! {
310 unsafe {
311 $view {
312 $(
313 $field: $ptr_view(
314 $crate::maybe_force_cast!($data.[<$($prefix_field)? $field>].add($self.$field.0), $type_ $(, $force)?),
315 $self.$field.1,
316 std::marker::PhantomData
317 ),
318 )*
319 $(
320 $field_ro: $ptr_view_ro(
321 $crate::maybe_force_cast!($data.[<$($prefix_field_ro)? $field_ro>].add($self.$field_ro.0), $type_ro $(, $force_ro)?),
322 $self.$field_ro.1,
323 std::marker::PhantomData
324 ),
325 )*
326 $(
327 $opt_field: if $self.$opt_field.1 > 0 {
328 Some($ptr_view(
329 $crate::maybe_force_cast!($data.[<$($prefix_opt_field)? $opt_field>].add($self.$opt_field.0), $type_opt $(, $force_opt)?),
330 $self.$opt_field.1,
331 std::marker::PhantomData
332 ))
333 } else {
334 None
335 },
336 )*
337 }
338 }
339 }
340 };
341}
342
343
344#[doc(hidden)]
362#[macro_export]
363macro_rules! info_method {
364 (
365 $info_type:ident, $([$model:ident],)?
366 $type_:ident,
367 [$($attr:ident: $len:expr),*],
368 [$($attr_ffi:ident: $len_ffi:ident $(* $multiplier:expr)?),*],
369 [$($attr_dyn:ident: $ffi_len_dyn:ident $(* $offset_mult:expr)?),*]
370 ) => {
371 paste::paste! {
372 #[doc = concat!(
373 "Returns a [`", stringify!([<Mj $type_:camel $info_type Info>]), "`] for the named ", stringify!($type_), ", ",
374 "containing the indices required to create views into [`Mj", stringify!($info_type), "`] arrays.\n\n",
375 "Call [`view`](", stringify!([<Mj $type_:camel $info_type Info>]), "::view) or ",
376 "[`try_view`](", stringify!([<Mj $type_:camel $info_type Info>]), "::try_view) on the result to obtain the actual view.\n\n",
377 "# Panics\n",
378 "Panics if `name` contains a `\\0` byte."
379 )]
380 #[allow(non_snake_case)]
381 pub fn $type_(&self, name: &str) -> Option<[<Mj $type_:camel $info_type Info>]> {
382 let model_ref = self$(.$model())?;
383 let id = model_ref.name_to_id(MjtObj::[<mjOBJ_ $type_:upper>], name)?;
384 let model_ffi = model_ref.ffi();
385
386 let id = id as usize;
387 $(
388 let $attr = (id * $len, $len);
389 )*
390 $(
391 let $attr_ffi = (
392 id * model_ffi.$len_ffi as usize $( * $multiplier)*,
393 model_ffi.$len_ffi as usize $( * $multiplier)*,
394 );
395 )*
396 $(
397 let $attr_dyn = {
398 let (dyn_start, dyn_len) = $crate::mj_model_dyn_range!(model_ref, id, $ffi_len_dyn);
399 (dyn_start $(* $offset_mult)?, dyn_len $(* $offset_mult)?)
400 };
401 )*
402
403 let model_signature = model_ffi.signature;
404 Some([<Mj $type_:camel $info_type Info>] { name: name.to_string(), id, model_signature, $($attr,)* $($attr_ffi,)* $($attr_dyn),* })
405 }
406 }
407 }
408}
409
410
411#[doc(hidden)]
430#[macro_export]
431macro_rules! info_with_view {
432 (
433 $info_type:ident, $name:ident,
434 [$($([$prefix_attr:ident])? $attr:ident: $type_:ty $([$force:ident])?),*],
435 [$($([$prefix_attr_ro:ident])? $attr_ro:ident: $type_ro:ty $([$force_ro:ident])?),*],
436 [$($([$prefix_opt_attr:ident])? $opt_attr:ident: $type_opt:ty $([$force_opt:ident])?),*]
437 $(,$generics:ty: $bound:ty)?
438 ) => {
439 paste::paste! {
440 #[doc = "Index ranges required to create views into [`Mj" $info_type "`] arrays for a " $name "."]
441 #[allow(non_snake_case)]
442 #[derive(Debug, Clone)]
443 pub struct [<Mj $name:camel $info_type Info>] {
444 pub name: String,
446 pub id: usize,
448 model_signature: u64,
449 $(
450 $attr: (usize, usize),
451 )*
452 $(
453 $attr_ro: (usize, usize),
454 )*
455 $(
456 $opt_attr: (usize, usize),
457 )*
458 }
459
460 impl [<Mj $name:camel $info_type Info>] {
461 pub fn model_signature(&self) -> u64 {
463 self.model_signature
464 }
465
466 #[doc = concat!(
467 "Returns a mutable view into the [`Mj", stringify!($info_type), "`] arrays for this ", stringify!($name), ".\n\n",
468 "Fields listed as read-only use [`PointerViewUnsafeMut`](crate::util::PointerViewUnsafeMut): ",
469 "read is safe, mutation requires [`as_mut_slice`](crate::util::PointerViewUnsafeMut::as_mut_slice) and `unsafe`.\n\n",
470 "# Errors\n",
471 "Returns [`SignatureMismatch`](", stringify!([<Mj $info_type Error>]), "::SignatureMismatch) if `",
472 stringify!($info_type), "` was built from a different model than this `Info`."
473 )]
474 pub fn try_view_mut<'p $(, $generics: $bound)?>(&self, [<$info_type:lower>]: &'p mut [<Mj $info_type>]$(<$generics>)?) -> Result<[<Mj $name:camel $info_type ViewMut>]<'p>, $crate::error::[<Mj $info_type Error>]> {
475 let destination_signature = [<$info_type:lower>].signature();
476 if self.model_signature != destination_signature {
477 return Err($crate::error::[<Mj $info_type Error>]::SignatureMismatch {
478 source: self.model_signature,
479 destination: destination_signature,
480 });
481 }
482 Ok(view_creator!(self, [<Mj $name:camel $info_type ViewMut>], [<$info_type:lower>].ffi(),
483 [$($([$prefix_attr])? $attr : $type_ $([$force])?),*],
484 [$($([$prefix_attr_ro])? $attr_ro : $type_ro $([$force_ro])?),*],
485 [$($([$prefix_opt_attr])? $opt_attr : $type_opt $([$force_opt])?),*],
486 $crate::util::PointerViewMut::new,
487 $crate::util::PointerViewUnsafeMut::new))
488 }
489
490 #[doc = concat!(
491 "Returns a mutable view into the [`Mj", stringify!($info_type), "`] arrays for this ", stringify!($name), ".\n\n",
492 "Fields listed as read-only use [`PointerViewUnsafeMut`](crate::util::PointerViewUnsafeMut): ",
493 "read is safe, mutation requires [`as_mut_slice`](crate::util::PointerViewUnsafeMut::as_mut_slice) and `unsafe`.\n\n",
494 "# Panics\n",
495 "Panics if `", stringify!($info_type), "` was built from a different model than this `Info`. ",
496 "Use [`try_view_mut`](Self::try_view_mut) to handle this as a `Result`."
497 )]
498 pub fn view_mut<'p $(, $generics: $bound)?>(&self, [<$info_type:lower>]: &'p mut [<Mj $info_type>]$(<$generics>)?) -> [<Mj $name:camel $info_type ViewMut>]<'p> {
499 self.try_view_mut([<$info_type:lower>]).unwrap_or_else(|_| panic!("model signature mismatch"))
500 }
501
502 #[doc = concat!(
503 "Returns an immutable view into the [`Mj", stringify!($info_type), "`] arrays for this ", stringify!($name), ".\n\n",
504 "# Errors\n",
505 "Returns [`SignatureMismatch`](", stringify!([<Mj $info_type Error>]), "::SignatureMismatch) if `",
506 stringify!($info_type), "` was built from a different model than this `Info`."
507 )]
508 pub fn try_view<'p $(, $generics: $bound)?>(&self, [<$info_type:lower>]: &'p [<Mj $info_type>]$(<$generics>)?) -> Result<[<Mj $name:camel $info_type View>]<'p>, $crate::error::[<Mj $info_type Error>]> {
509 let destination_signature = [<$info_type:lower>].signature();
510 if self.model_signature != destination_signature {
511 return Err($crate::error::[<Mj $info_type Error>]::SignatureMismatch {
512 source: self.model_signature,
513 destination: destination_signature,
514 });
515 }
516 Ok(view_creator!(self, [<Mj $name:camel $info_type View>], [<$info_type:lower>].ffi(),
517 [$($([$prefix_attr])? $attr : $type_ $([$force])?),*],
518 [$($([$prefix_attr_ro])? $attr_ro : $type_ro $([$force_ro])?),*],
519 [$($([$prefix_opt_attr])? $opt_attr : $type_opt $([$force_opt])?),*],
520 $crate::util::PointerView::new,
521 $crate::util::PointerView::new))
522 }
523
524 #[doc = concat!(
525 "Returns an immutable view into the [`Mj", stringify!($info_type), "`] arrays for this ", stringify!($name), ".\n\n",
526 "# Panics\n",
527 "Panics if `", stringify!($info_type), "` was built from a different model than this `Info`. ",
528 "Use [`try_view`](Self::try_view) to handle this as a `Result`."
529 )]
530 pub fn view<'p $(, $generics: $bound)?>(&self, [<$info_type:lower>]: &'p [<Mj $info_type>]$(<$generics>)?) -> [<Mj $name:camel $info_type View>]<'p> {
531 self.try_view([<$info_type:lower>]).unwrap_or_else(|_| panic!("model signature mismatch"))
532 }
533 }
534
535 #[doc = "Mutable view into [`Mj" $info_type "`] arrays for a " $name ".\n\n"
536 "Read-write fields are [`PointerViewMut`](crate::util::PointerViewMut); "
537 "read-only fields are [`PointerViewUnsafeMut`](crate::util::PointerViewUnsafeMut), "
538 "which require [`as_mut_slice`](crate::util::PointerViewUnsafeMut::as_mut_slice) and explicit `unsafe` to mutate."]
539 #[allow(non_snake_case)]
540 #[derive(Debug)]
541 pub struct [<Mj $name:camel $info_type ViewMut>]<'d> {
542 $(
543 #[doc = concat!("Mutable view of `", stringify!($attr), "`.")]
544 pub $attr: $crate::util::PointerViewMut<'d, $type_>,
545 )*
546 $(
547 #[doc = concat!("Read-only view of `", stringify!($attr_ro), "`. Requires `unsafe` for mutation.")]
548 pub $attr_ro: $crate::util::PointerViewUnsafeMut<'d, $type_ro>,
549 )*
550 $(
551 #[doc = concat!("Optional mutable view of `", stringify!($opt_attr), "`.")]
552 pub $opt_attr: Option<$crate::util::PointerViewMut<'d, $type_opt>>,
553 )*
554 }
555
556 impl [<Mj $name:camel $info_type ViewMut>]<'_> {
557 pub fn zero(&mut self) {
559 $(
560 self.$attr.fill(bytemuck::Zeroable::zeroed());
561 )*
562 $(
563 if let Some(x) = &mut self.$opt_attr {
564 x.fill(bytemuck::Zeroable::zeroed());
565 }
566 )*
567 }
568 }
569
570 #[doc = "Immutable view into [`Mj" $info_type "`] arrays for a " $name "."]
571 #[allow(non_snake_case)]
572 #[derive(Debug)]
573 pub struct [<Mj $name:camel $info_type View>]<'d> {
574 $(
575 #[doc = concat!("View of `", stringify!($attr), "`.")]
576 pub $attr: $crate::util::PointerView<'d, $type_>,
577 )*
578 $(
579 #[doc = concat!("View of `", stringify!($attr_ro), "`.")]
580 pub $attr_ro: $crate::util::PointerView<'d, $type_ro>,
581 )*
582 $(
583 #[doc = concat!("Optional view of `", stringify!($opt_attr), "`.")]
584 pub $opt_attr: Option<$crate::util::PointerView<'d, $type_opt>>,
585 )*
586 }
587 }
588 };
589}
590
591#[doc(hidden)]
620#[macro_export]
621macro_rules! getter_setter {
622
623 (@cast force { $($content:tt)* } ) => {
626 $crate::util::force_cast($($content)*)
627 };
628
629 (@cast { $($content:tt)* } ) => {
630 $($content)*.into()
631 };
632
633 (get, [$($([$ffi:ident])? $name:ident $(+ $symbol:tt)?: bool; $comment:expr);* $(;)?]) => {paste::paste!{
636 $(
637 #[doc = concat!("Check ", $comment)]
638 pub fn [<$name:camel:snake $($symbol)?>](&self) -> bool {
639 (self$(.$ffi())?.$name as i32) != 0
640 }
641 )*
642 }};
643
644 (get, [$($([$ffi:ident $(,$ffi_mut:ident)?])? $((allow_mut = $cfg_mut:literal))? $name:ident $(+ $symbol:tt)?: & $type:ty; $comment:expr);* $(;)?]) => {paste::paste!{
645 $(
646 #[doc = concat!("Return an immutable reference to ", $comment)]
647 pub fn [<$name:camel:snake $($symbol)?>](&self) -> &$type {
648 &self$(.$ffi())?.$name
649 }
650
651 $crate::eval_or_expand! {
652 @eval $($cfg_mut)? {
653 #[doc = concat!("Return a mutable reference to ", $comment)]
654 pub fn [<$name:camel:snake _mut>](&mut self) -> &mut $type {
655 #[allow(unused_unsafe)]
656 unsafe { &mut self$(.$($ffi_mut())?)?.$name }
657 }
658 }
659 }
660 )*
661 }};
662
663 (get, [$($([$ffi:ident])? $name:ident $(+ $symbol:tt)?: $type:ty $([$cast_type:tt])?; $comment:expr);* $(;)?]) => {paste::paste!{
664 $(
665 #[doc = concat!("Return value of ", $comment)]
666 pub fn [<$name:camel:snake $($symbol)?>](&self) -> $type {
667 #[allow(unused_unsafe)]
668 unsafe { $crate::getter_setter!(@cast $($cast_type)? { self$(.$ffi())?.$name }) }
669 }
670 )*
671 }};
672
673 (set, [$($([$ffi_mut:ident])? $name:ident: $type:ty $([$cast_type:tt])? $({$check:expr , $reason:literal})? $(=> $err:ty)?; $comment:expr);* $(;)?]) => {
674 paste::paste!{
675 $(
676 #[doc = concat!("Set ", $comment $(, "\n\n# Errors\nReturns ", $reason, ".")?)]
677 pub fn [<set_ $name:camel:snake>](&mut self, value: $type) $(-> Result<(), $err>)? {
678 $(($check)(value)?;)?
679 #[allow(unused_unsafe)]
680 unsafe { self$(.$ffi_mut())?.$name = $crate::getter_setter!(@cast $($cast_type)? { value }) };
681 $(Ok::<(), $err>(()))?
682 }
683 )*
684 }
685 };
686
687 (with, [$($inner:tt)*]) => {
689 $crate::getter_setter!(@with_body [Self], [$($inner)*]);
690 };
691 ([&] with, [$($inner:tt)*]) => {
692 $crate::getter_setter!(@with_body [&mut Self], [$($inner)*]);
693 };
694
695 (@with_body [$ret_ty:ty], [$($([$ffi_mut:ident])? $name:ident: $type:ty $([$cast_type:tt])? $({$check:expr , $reason:literal})?; $comment:expr);* $(;)?]) => {
696 paste::paste!{
697 $(
698 #[allow(unused_mut)]
699 #[doc = concat!("Builder method for setting ", $comment $(, "\n\n# Panics\nPanics with ", $reason, ".")?)]
700 pub fn [<with_ $name:camel:snake>](mut self: $ret_ty, value: $type) -> $ret_ty {
701 $(($check)(value).expect("invalid builder argument");)?
702 #[allow(unused_unsafe)]
703 unsafe { self$(.$ffi_mut())?.$name = $crate::getter_setter!(@cast $($cast_type)? { value }) };
704 self
705 }
706 )*
707 }
708 };
709
710 (get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : bool ; $comment:expr );* $(;)?]) => {
712 $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)? : bool ; $comment );* ]);
713 $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : bool ; $comment );* ]);
714 };
715
716 (get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : $type:ty $([$cast_type:tt])? $({$check:expr , $reason:literal})? $(=> $err:ty)?; $comment:expr );* $(;)?]) => {
717 $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)? : $type $([$cast_type])? ; $comment );* ]);
718 $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : $type $([$cast_type])? $({$check , $reason})? $(=> $err)?; $comment );* ]);
719 };
720
721 ($([$token:tt])? with, get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : bool ; $comment:expr );* $(;)?]) => {
723 $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)? : bool ; $comment );* ]);
724 $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : bool ; $comment );* ]);
725 $crate::getter_setter!($([$token])? with, [ $($([$ffi_mut])? $name : bool ; $comment );* ]);
726 };
727
728 ($([$token:tt])? with, get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : $type:ty $([$cast_type:tt])? $({$check:expr , $reason:literal})? $(=> $err:ty)?; $comment:expr );* $(;)?]) => {
729 $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)?: $type $([$cast_type])? ; $comment );* ]);
730 $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : $type $([$cast_type])? $({$check , $reason})? $(=> $err)?; $comment );* ]);
731 $crate::getter_setter!($([$token])? with, [ $($([$ffi_mut])? $name : $type $([$cast_type])? $({$check , $reason})?; $comment );* ]);
732 };
733
734 ($([$token:tt])? with, get, [$( $([$ffi: ident, $ffi_mut:ident])? $((allow_mut = $allow_mut:literal))? $name:ident $(+ $symbol:tt)? : & $type:ty $({$check:expr , $reason:literal})? ; $comment:expr );* $(;)?]) => {
735 $crate::getter_setter!(get, [ $($([$ffi, $ffi_mut])? $((allow_mut = $allow_mut))? $name $(+ $symbol)? : & $type ; $comment );* ]);
736 $crate::getter_setter!($([$token])? with, [ $( $([$ffi_mut])? $name : $type $({$check , $reason})? ; $comment );* ]);
737 };
738}
739
740
741#[doc(hidden)]
742#[macro_export]
743macro_rules! builder_setters {
745 ($($name:ident: $type:ty $(where $generic_type:ident: $generic:path)?; $comment:expr);* $(;)?) => {
746 $(
747 #[doc = concat!("Set ", $comment)]
748 pub fn $name$(<$generic_type: $generic>)?(mut self, value: $type) -> Self {
749 self.$name = value.into();
750 self
751 }
752 )*
753 };
754}
755
756#[doc(hidden)]
760#[macro_export]
761macro_rules! array_mut_doc {
762 (unsafe, $doc:literal) => {
763 concat!("Mutable slice of the ", $doc, " array.\n\n# Safety\n\nDirect mutation of this array bypasses MuJoCo's internal consistency checks. The caller must ensure that all values written remain valid for MuJoCo's internal state.")
764 };
765 ($doc:literal) => {
766 concat!("Mutable slice of the ", $doc, " array.")
767 };
768}
769
770#[doc(hidden)]
786#[macro_export]
787macro_rules! array_slice_dyn {
788 ($($((mut = $unsafe_mut:ident))? $name:ident: $($as_ptr:ident $as_mut_ptr:ident)? &[$type:ty $([$force:ident])?; $doc:literal; $($len_accessor:tt)*]),*) => {
790 paste::paste! {
791 $(
792 #[doc = concat!("Immutable slice of the ", $doc," array.")]
793 pub fn [<$name:camel:snake>](&self) -> &[$type] {
794 let length = self.$($len_accessor)* as usize;
795 let ptr = $crate::maybe_force_cast!(self.ffi().$name$(.$as_ptr())?, $type $(, $force)?);
796 if ptr.is_null() || length == 0 {
797 return &[];
798 }
799 unsafe { std::slice::from_raw_parts(ptr, length) }
800 }
801
802 #[doc = $crate::array_mut_doc!($($unsafe_mut,)? $doc)]
803 pub $($unsafe_mut)? fn [<$name:camel:snake _mut>](&mut self) -> &mut [$type] {
804 let length = self.$($len_accessor)* as usize;
805 let ptr = $crate::maybe_force_cast!(unsafe { self.ffi_mut().$name$(.$as_mut_ptr())? }, $type $(, $force)?);
806 if ptr.is_null() || length == 0 {
807 return &mut [];
808 }
809 unsafe { std::slice::from_raw_parts_mut(ptr, length) }
810 }
811 )*
812 }
813 };
814
815 (summed { $( $(($unsafe_mut:ident))? $name:ident: &[$type:ty; $doc:literal; [$multiplier:literal ; ($($len_array:tt)*) ; ($($len_array_length:tt)*)]]),* }) => {
817 paste::paste! {
818 $(
819 #[doc = concat!("Immutable slice of the ", $doc," array.")]
820 pub fn [<$name:camel:snake>](&self) -> &[[$type; $multiplier]] {
821 let length_array_length = self.$($len_array_length)* as usize;
822 let data_ptr = self.ffi().$name;
823 let length_ptr = self.$($len_array)*;
824 if data_ptr.is_null() || length_ptr.is_null() || length_array_length == 0 {
825 return &[];
826 }
827
828 let length = unsafe { std::slice::from_raw_parts(
829 length_ptr,
830 length_array_length
831 ).iter().map(|&x| x as u32).sum::<u32>() as usize };
832
833 if length == 0 {
834 return &[];
835 }
836
837 unsafe { std::slice::from_raw_parts($crate::maybe_force_cast!(data_ptr, [$type; $multiplier], force), length) }
838 }
839
840 #[doc = $crate::array_mut_doc!($($unsafe_mut,)? $doc)]
841 pub $($unsafe_mut)? fn [<$name:camel:snake _mut>](&mut self) -> &mut [[$type; $multiplier]] {
842 let length_array_length = self.$($len_array_length)* as usize;
843 let data_ptr = unsafe { self.ffi_mut().$name };
844 let length_ptr = self.$($len_array)*;
845 if data_ptr.is_null() || length_ptr.is_null() || length_array_length == 0 {
846 return &mut [];
847 }
848
849 let length = unsafe { std::slice::from_raw_parts(
850 length_ptr,
851 length_array_length
852 ).iter().map(|&x| x as u32).sum::<u32>() as usize };
853
854 if length == 0 {
855 return &mut [];
856 }
857
858 unsafe { std::slice::from_raw_parts_mut($crate::maybe_force_cast!(data_ptr, [$type; $multiplier], force), length) }
859 }
860 )*
861 }
862 };
863
864 (sublen_dep {$( $(($unsafe_mut:ident))? $name:ident: $($as_ptr:ident $as_mut_ptr:ident)? &[[$type:ty; $($inner_len_accessor:tt)*] $([$force:ident])?; $doc:literal; $($len_accessor:tt)*]),*}) => {
866 paste::paste! {
867 $(
868 #[doc = concat!("Immutable slice of the ", $doc," array.")]
869 pub fn [<$name:camel:snake>](&self) -> &[$type] {
870 let length = self.$($len_accessor)* as usize * (self.$($inner_len_accessor)*) as usize;
871 let ptr = $crate::maybe_force_cast!(self.ffi().$name$(.$as_ptr())?, $type $(, $force)?);
872 if ptr.is_null() || length == 0 {
873 return &[];
874 }
875
876 unsafe { std::slice::from_raw_parts(ptr, length) }
877 }
878
879 #[doc = $crate::array_mut_doc!($($unsafe_mut,)? $doc)]
880 pub $($unsafe_mut)? fn [<$name:camel:snake _mut>](&mut self) -> &mut [$type] {
881 let length = self.$($len_accessor)* as usize * (self.$($inner_len_accessor)*) as usize;
882 let ptr = $crate::maybe_force_cast!(unsafe { self.ffi_mut().$name$(.$as_mut_ptr())? }, $type $(, $force)?);
883 if ptr.is_null() || length == 0 {
884 return &mut [];
885 }
886 unsafe { std::slice::from_raw_parts_mut(ptr, length) }
887 }
888 )*
889 }
890 };
891}
892
893#[doc(hidden)]
912#[macro_export]
913macro_rules! c_str_as_str_method {
914 (get {$($([$ffi:ident])? $name:ident $([$sub_index_name:ident: $sub_index_type:ty])?; $comment:literal; )*}) => {
915 $(
916 #[doc = concat!("Returns ", $comment, "\n\n# Panics", "\nPanics if the buffer has no NUL terminator or if the resulting string contains invalid UTF-8.")]
917 pub fn $name(&self $(, $sub_index_name: $sub_index_type)? ) -> &str {
918 let bytes: &[u8] = bytemuck::cast_slice(&self$(.$ffi())?.$name$([$sub_index_name])?[..]);
919 std::ffi::CStr::from_bytes_until_nul(bytes)
920 .expect("no NUL terminator in C string buffer")
921 .to_str().unwrap()
922 }
923 )*
924 };
925
926 (set {$($([$ffi:ident])? $name:ident $([$sub_index_name:ident: $sub_index_type:ty])?; $comment:literal; )*}) => {paste::paste!{
927 $(
928 #[doc = concat!("Sets ", $comment, "\n\n# Panics", "\nPanics when `", stringify!($name), "` contains invalid ASCII, an interior NUL byte, or is too long.")]
929 pub fn [<set_ $name>](&mut self, $($sub_index_name: $sub_index_type,)? $name: &str) {
930 $crate::util::write_ascii_to_buf(
931 &mut self$(.$ffi())?.$name$([$sub_index_name])?,
932 $name,
933 );
934 }
935 )*
936 }};
937
938 (with {$($([$ffi:ident])? $name:ident $([$sub_index_name:ident: $sub_index_type:ty])?; $comment:literal; )*}) => {paste::paste!{
939 $(
940 #[doc = concat!("Builder method for setting ", $comment, "\n\n# Panics", "\nPanics when `", stringify!($name), "` contains invalid ASCII, an interior NUL byte, or is too long.")]
941 pub fn [<with_ $name>](mut self, $($sub_index_name: $sub_index_type,)? $name: &str) -> Self {
942 $crate::util::write_ascii_to_buf(
943 &mut self$(.$ffi())?.$name$([$sub_index_name])?,
944 $name,
945 );
946 self
947 }
948 )*
949 }};
950
951 (with, get, set {$($other:tt)*}) => {
953 $crate::c_str_as_str_method!(get {$($other)*});
954 $crate::c_str_as_str_method!(set {$($other)*});
955 $crate::c_str_as_str_method!(with {$($other)*});
956 };
957
958 (get, set {$($other:tt)*}) => {
959 $crate::c_str_as_str_method!(get {$($other)*});
960 $crate::c_str_as_str_method!(set {$($other)*});
961 };
962
963 (with, set {$($other:tt)*}) => {
964 $crate::c_str_as_str_method!(set {$($other)*});
965 $crate::c_str_as_str_method!(with {$($other)*});
966 };
967
968 (with, get {$($other:tt)*}) => {
969 $crate::c_str_as_str_method!(get {$($other)*});
970 $crate::c_str_as_str_method!(with {$($other)*});
971 };
972}
973
974#[doc(hidden)]
976#[macro_export]
977macro_rules! assert_relative_eq {
978 ($a:expr, $b:expr, epsilon = $eps:expr) => {{
979 let (a, b, eps) = ($a as f64, $b as f64, $eps as f64);
980 assert!((a - b).abs() <= eps, "left={:?} right={:?} eps={:?}", a, b, eps);
981 }};
982}
983
984
985#[doc(hidden)]
989#[macro_export]
990macro_rules! cast_mut_info {
991 ($value:expr $(, $debug_expr:expr)?) => {
992 {
993 match bytemuck::checked::try_cast_mut($value) {
994 Ok(v) => v,
995 Err(e) => {
996 let evaluated = format!("{:?}", $value);
997 #[allow(unused)]
998 let mut debug_info = String::new();
999 $(
1000 debug_info = format!(" (debug info: '{} = {}')", stringify!($debug_expr), $debug_expr);
1001 )?
1002
1003 panic!(
1004 "failed to cast expression '{}', which evaluates to '{}' into requested type (error: {})\
1005 {debug_info} --- \
1006 most likely you have a bug in your program.",
1007 stringify!($value), evaluated, e
1008 );
1009 }
1010 }
1011 }
1012 };
1013}
1014
1015pub fn assert_mujoco_version() {
1022 let linked_version = unsafe { mj_version() as u32 };
1025 let mujoco_rs_version_string = option_env!("CARGO_PKG_VERSION").unwrap_or_else(|| "unknown+mj-unknown");
1026 assert_eq!(
1027 linked_version, mjVERSION_HEADER,
1028 "linked MuJoCo version value ({linked_version}) does not match expected version value ({mjVERSION_HEADER}), \
1029 with which MuJoCo-rs {mujoco_rs_version_string} FFI bindings were generated.",
1030 );
1031}
1032
1033
1034#[inline(always)]
1042pub unsafe fn force_cast<T, U>(val: T) -> U {
1043 const {
1044 assert!(std::mem::size_of::<T>() == std::mem::size_of::<U>());
1046 assert!(std::mem::align_of::<T>() == std::mem::align_of::<U>());
1047 }
1048 #[repr(C)]
1049 union Transmuter<T, U> {
1050 from: std::mem::ManuallyDrop<T>,
1051 to: std::mem::ManuallyDrop<U>,
1052 }
1053 unsafe { std::mem::ManuallyDrop::into_inner(Transmuter { from: std::mem::ManuallyDrop::new(val) }.to) }
1056}
1057
1058
1059#[inline(always)]
1070pub const fn assert_ptr_cast_valid<Src, Dst>(_ptr: *const Src) {
1071 const {
1072 assert!(std::mem::size_of::<Dst>().is_multiple_of(std::mem::size_of::<Src>()),
1073 "ptr cast: target size must be a multiple of source size");
1074
1075 assert!(std::mem::align_of::<Src>() == std::mem::align_of::<Dst>(),
1078 "ptr cast: source alignment must be == target alignment");
1079 }
1080}
1081
1082#[doc(hidden)]
1086#[macro_export]
1087macro_rules! maybe_force_cast {
1088 ($ptr:expr, $type:ty) => { $ptr };
1089 ($ptr:expr, $type:ty, force) => {{
1090 let ptr = $ptr;
1091 $crate::util::assert_ptr_cast_valid::<_, $type>(ptr as *const _);
1092 ptr.cast::<$type>()
1093 }};
1094}
1095
1096
1097pub trait LockUnpoison<T> {
1102 fn lock_unpoison(&self) -> MutexGuard<'_, T>;
1104}
1105
1106impl<T> LockUnpoison<T> for Mutex<T> {
1108 fn lock_unpoison(&self) -> MutexGuard<'_, T> {
1109 match self.lock() {
1110 Ok(lock) => lock,
1111 Err(e) => {
1112 self.clear_poison();
1113 e.into_inner()
1114 }
1115 }
1116 }
1117}
1118
1119#[cfg(feature = "viewer")]
1120pub(crate) trait ThreeWayMerge {
1127 fn merge(&mut self, other: &mut Self, other_prev: &mut Self);
1129}
1130
1131#[cfg(feature = "viewer")]
1132impl<T: Copy + PartialEq> ThreeWayMerge for T {
1133 #[inline]
1134 fn merge(&mut self, other: &mut Self, other_prev: &mut Self) {
1135 if *other != *other_prev {
1136 *self = *other;
1137 }
1138
1139 *other = *self;
1140 *other_prev = *other;
1141 }
1142}
1143
1144#[cfg(test)]
1145mod tests {
1146 use std::sync::{Arc, Mutex};
1147 use std::ffi::c_char;
1148 use super::LockUnpoison;
1149
1150 #[test]
1152 fn test_lock_unpoison_recovers_poisoned_mutex() {
1153 let mutex = Arc::new(Mutex::new(42_i32));
1154 let mutex_clone = Arc::clone(&mutex);
1155
1156 let _ = std::panic::catch_unwind(move || {
1158 let _guard = mutex_clone.lock().unwrap();
1159 panic!("intentional panic to poison mutex");
1160 });
1161
1162 assert!(mutex.lock().is_err(), "mutex should be poisoned");
1164
1165 let value = *mutex.lock_unpoison();
1167 assert_eq!(value, 42, "inner value must be preserved after unpoison");
1168
1169 assert!(mutex.lock().is_ok(), "mutex should no longer be poisoned");
1171 }
1172
1173 fn non_negative(v: i32) -> Result<(), &'static str> {
1175 if v >= 0 { Ok(()) } else { Err("must be non-negative") }
1176 }
1177
1178 fn first_non_negative(a: [f64; 2]) -> Result<(), &'static str> {
1180 if a[0] >= 0.0 { Ok(()) } else { Err("first must be non-negative") }
1181 }
1182
1183 #[test]
1188 #[allow(clippy::bool_assert_comparison)]
1189 #[allow(dead_code)]
1192 fn test_getter_setter_all_arms() {
1193 use crate::getter_setter as gs;
1194
1195 struct GetBool { flag: i32 }
1198 impl GetBool { gs!(get, [flag: bool; "flag.";]); }
1199 assert_eq!(GetBool { flag: 1 }.flag(), true);
1200 assert_eq!(GetBool { flag: 0 }.flag(), false);
1201
1202 struct GetRef { arr: [f64; 3] }
1204 impl GetRef { gs!(get, [arr: &[f64; 3]; "array.";]); }
1205 let mut gr = GetRef { arr: [1.0, 2.0, 3.0] };
1206 assert_eq!(gr.arr(), &[1.0, 2.0, 3.0]);
1207 gr.arr_mut()[0] = 9.0;
1208 assert_eq!(gr.arr(), &[9.0, 2.0, 3.0]);
1209
1210 struct GetRefNoMut { arr: [f64; 2] }
1211 impl GetRefNoMut { gs!(get, [(allow_mut = false) arr: &[f64; 2]; "array.";]); }
1212 assert_eq!(GetRefNoMut { arr: [4.0, 5.0] }.arr(), &[4.0, 5.0]);
1213
1214 struct GetVal { val: i32 }
1216 impl GetVal { gs!(get, [val + _renamed: i32; "value.";]); }
1217 assert_eq!(GetVal { val: 7 }.val_renamed(), 7);
1218
1219 struct ForceGet { v: i32 }
1221 impl ForceGet { gs!(get, [v: i32 [force]; "v.";]); }
1222 assert_eq!(ForceGet { v: 42 }.v(), 42);
1223
1224 struct Set { a: i32, b: i32 }
1227 impl Set {
1228 gs!(set, [
1229 a: i32; "a.";
1230 b: i32 { non_negative, "`Err` when negative" } => &'static str; "b.";
1231 ]);
1232 }
1233 let mut s = Set { a: 0, b: 0 };
1234 s.set_a(5);
1235 assert_eq!(s.a, 5);
1236 assert_eq!(s.set_b(3), Ok(()));
1237 assert_eq!(s.b, 3);
1238 assert_eq!(s.set_b(-1), Err("must be non-negative"));
1239 assert_eq!(s.b, 3); struct ForceSet { a: i32, b: i32 }
1243 impl ForceSet {
1244 gs!(set, [
1245 a: i32 [force]; "a.";
1246 b: i32 [force] { non_negative, "`Err` when negative" } => &'static str; "b.";
1247 ]);
1248 }
1249 let mut fs = ForceSet { a: 0, b: 0 };
1250 fs.set_a(8);
1251 assert_eq!(fs.a, 8);
1252 assert_eq!(fs.set_b(2), Ok(()));
1253 assert_eq!(fs.set_b(-1), Err("must be non-negative"));
1254 assert_eq!(fs.b, 2);
1255
1256 struct With { v: i32 }
1258 impl With { gs!(with, [v: i32 { non_negative, "`Err` when negative" }; "v.";]); }
1259 assert_eq!(With { v: 0 }.with_v(6).v, 6);
1260
1261 struct RefWith { v: i32 }
1263 impl RefWith { gs!([&] with, [v: i32 { non_negative, "`Err` when negative" }; "v.";]); }
1264 let mut rw = RefWith { v: 0 };
1265 rw.with_v(4);
1266 assert_eq!(rw.v, 4);
1267
1268 struct ForceWith { v: i32 }
1270 impl ForceWith { gs!(with, [v: i32 [force] { non_negative, "`Err` when negative" }; "v.";]); }
1271 assert_eq!(ForceWith { v: 0 }.with_v(3).v, 3);
1272
1273 struct ForceRefWith { v: i32 }
1274 impl ForceRefWith { gs!([&] with, [v: i32 [force] { non_negative, "`Err` when negative" }; "v.";]); }
1275 let mut frw = ForceRefWith { v: 0 };
1276 frw.with_v(5);
1277 assert_eq!(frw.v, 5);
1278
1279 struct ForceGetSet { v: i32 }
1282 impl ForceGetSet { gs!(get, set, [v: i32 [force] { non_negative, "`Err` when negative" } => &'static str; "v.";]); }
1283 let mut fgs = ForceGetSet { v: 0 };
1284 assert_eq!(fgs.set_v(11), Ok(()));
1285 assert_eq!(fgs.v(), 11);
1286 assert_eq!(fgs.set_v(-1), Err("must be non-negative"));
1287
1288 struct GetSetBool { flag: i32 }
1290 impl GetSetBool { gs!(get, set, [flag: bool; "flag.";]); }
1291 let mut gsb = GetSetBool { flag: 1 };
1292 assert_eq!(gsb.flag(), true);
1293 gsb.set_flag(false);
1294 assert_eq!(gsb.flag(), false);
1295
1296 struct GetSetVal { count: i32, checked: i32 }
1297 impl GetSetVal {
1298 gs!(get, set, [
1299 count: i32; "count.";
1300 checked: i32 { non_negative, "`Err` when negative" } => &'static str; "checked.";
1301 ]);
1302 }
1303 let mut gsv = GetSetVal { count: 5, checked: 0 };
1304 gsv.set_count(10);
1305 assert_eq!(gsv.count(), 10);
1306 assert_eq!(gsv.set_checked(7), Ok(()));
1307 assert_eq!(gsv.checked(), 7);
1308 assert_eq!(gsv.set_checked(-1), Err("must be non-negative"));
1309 assert_eq!(gsv.checked(), 7);
1310
1311 struct WithGetSetBool { flag: i32 }
1313 impl WithGetSetBool { gs!(with, get, set, [flag: bool; "flag.";]); }
1314 assert_eq!(WithGetSetBool { flag: 0 }.with_flag(true).flag(), true);
1315
1316 struct RefWithGetSetBool { flag: i32 }
1317 impl RefWithGetSetBool { gs!([&] with, get, set, [flag: bool; "flag.";]); }
1318 let mut rwgsb = RefWithGetSetBool { flag: 0 };
1319 rwgsb.with_flag(true);
1320 assert_eq!(rwgsb.flag(), true);
1321
1322 struct WithGetSetVal { v: i32 }
1324 impl WithGetSetVal { gs!(with, get, set, [v: i32; "v.";]); }
1325 let mut wgsv = WithGetSetVal { v: 0 }.with_v(3);
1326 assert_eq!(wgsv.v(), 3);
1327 wgsv.set_v(4);
1328 assert_eq!(wgsv.v(), 4);
1329
1330 struct RefWithGetSetVal { v: i32, w: i32 }
1331 impl RefWithGetSetVal {
1332 gs!([&] with, get, set, [
1333 v: i32; "v.";
1334 w: i32 { non_negative, "`Err` when negative" } => &'static str; "w.";
1335 ]);
1336 }
1337 let mut rwgsv = RefWithGetSetVal { v: 0, w: 0 };
1338 rwgsv.with_v(1);
1339 assert_eq!(rwgsv.v(), 1);
1340 assert_eq!(rwgsv.set_w(2), Ok(()));
1341 assert_eq!(rwgsv.w(), 2);
1342 assert_eq!(rwgsv.set_w(-1), Err("must be non-negative"));
1343
1344 struct ForceWithGetSet { v: i32 }
1346 impl ForceWithGetSet { gs!(with, get, set, [v: i32 [force] { non_negative, "`Err` when negative" } => &'static str; "v.";]); }
1347 let fwgs = ForceWithGetSet { v: 0 }.with_v(9);
1348 assert_eq!(fwgs.v(), 9);
1349
1350 struct ForceRefWithGetSet { v: i32 }
1351 impl ForceRefWithGetSet { gs!([&] with, get, set, [v: i32 [force]; "v.";]); }
1352 let mut frwgs = ForceRefWithGetSet { v: 0 };
1353 frwgs.with_v(6);
1354 assert_eq!(frwgs.v(), 6);
1355
1356 struct WithGetRef { arr: [f64; 3] }
1358 impl WithGetRef { gs!(with, get, [arr: &[f64; 3]; "array.";]); }
1359 let wgr = WithGetRef { arr: [0.0; 3] }.with_arr([1.0, 2.0, 3.0]);
1360 assert_eq!(wgr.arr(), &[1.0, 2.0, 3.0]);
1361
1362 struct RefWithGetRef { arr: [f64; 2] }
1363 impl RefWithGetRef { gs!([&] with, get, [arr: &[f64; 2] { first_non_negative, "`Err` when the first element is negative" }; "array.";]); }
1364 let mut rwgr = RefWithGetRef { arr: [0.0; 2] };
1365 rwgr.with_arr([5.0, 6.0]);
1366 assert_eq!(rwgr.arr(), &[5.0, 6.0]);
1367 }
1368
1369 #[test]
1376 #[should_panic(expected = "invalid builder argument")]
1377 fn test_with_check_panics() {
1378 struct S { v: i32 }
1379 impl S { crate::getter_setter!(with, [v: i32 { non_negative, "`Err` when negative" }; "v.";]); }
1380 S { v: 0 }.with_v(-1);
1381 }
1382
1383 #[test]
1385 #[should_panic(expected = "invalid builder argument")]
1386 fn test_ref_with_check_panics() {
1387 struct S { v: i32 }
1388 impl S { crate::getter_setter!([&] with, [v: i32 { non_negative, "`Err` when negative" }; "v.";]); }
1389 S { v: 0 }.with_v(-1);
1390 }
1391
1392 #[test]
1394 #[should_panic(expected = "invalid builder argument")]
1395 fn test_force_with_check_panics() {
1396 struct S { v: i32 }
1397 impl S { crate::getter_setter!(with, [v: i32 [force] { non_negative, "`Err` when negative" }; "v.";]); }
1398 S { v: 0 }.with_v(-1);
1399 }
1400
1401 #[test]
1403 #[should_panic(expected = "invalid builder argument")]
1404 fn test_force_ref_with_check_panics() {
1405 struct S { v: i32 }
1406 impl S { crate::getter_setter!([&] with, [v: i32 [force] { non_negative, "`Err` when negative" }; "v.";]); }
1407 S { v: 0 }.with_v(-1);
1408 }
1409
1410 #[test]
1412 #[should_panic(expected = "invalid builder argument")]
1413 #[allow(dead_code)] fn test_with_get_set_aggregate_check_panics() {
1415 struct S { w: i32 }
1416 impl S {
1417 crate::getter_setter!([&] with, get, set, [
1418 w: i32 { non_negative, "`Err` when negative" } => &'static str; "w.";
1419 ]);
1420 }
1421 S { w: 0 }.with_w(-1);
1422 }
1423
1424 #[test]
1426 #[should_panic(expected = "invalid builder argument")]
1427 #[allow(dead_code)] fn test_force_with_get_set_aggregate_check_panics() {
1429 struct S { v: i32 }
1430 impl S {
1431 crate::getter_setter!(with, get, set, [
1432 v: i32 [force] { non_negative, "`Err` when negative" } => &'static str; "v.";
1433 ]);
1434 }
1435 S { v: 0 }.with_v(-1);
1436 }
1437
1438 #[test]
1440 #[should_panic(expected = "invalid builder argument")]
1441 #[allow(dead_code)] fn test_with_get_ref_aggregate_check_panics() {
1443 struct S { arr: [f64; 2] }
1444 impl S {
1445 crate::getter_setter!([&] with, get, [arr: &[f64; 2] { first_non_negative, "`Err` when the first element is negative" }; "array.";]);
1446 }
1447 S { arr: [0.0; 2] }.with_arr([-1.0, 0.0]);
1448 }
1449
1450 #[test]
1452 fn test_cast_mut_info_both_arms() {
1453 let mut val: [u8; 4] = [7, 0, 0, 0];
1454
1455 let r: &mut [u8; 4] = crate::cast_mut_info!(&mut val);
1457 r[0] = 42;
1458 assert_eq!(val[0], 42);
1459
1460 let idx: usize = 0;
1462 let r2: &mut [u8; 4] = crate::cast_mut_info!(&mut val, idx);
1463 r2[0] = 99;
1464 assert_eq!(val[0], 99);
1465 }
1466
1467 #[test]
1471 fn test_c_str_as_str_method_combination_arms() {
1472 struct GetSet { name: [c_char; 16] }
1474 impl GetSet {
1475 crate::c_str_as_str_method!(get, set { name; "name."; });
1476 }
1477
1478 struct WithSet { label: [c_char; 16] }
1480 impl WithSet {
1481 crate::c_str_as_str_method!(with, set { label; "label."; });
1482 }
1483
1484 struct WithGet { title: [c_char; 16] }
1486 impl WithGet {
1487 crate::c_str_as_str_method!(with, get { title; "title."; });
1488 }
1489
1490 let mut gs = GetSet { name: [0; 16] };
1492 gs.set_name("hello");
1493 assert_eq!(gs.name(), "hello");
1494
1495 let ws = WithSet { label: [0; 16] }.with_label("world");
1497 let bytes: &[u8] = bytemuck::cast_slice(&ws.label[..]);
1498 assert!(bytes.starts_with(b"world\0"), "with_label did not write label");
1499 let mut ws2 = WithSet { label: [0; 16] };
1501 ws2.set_label("bye");
1502 let bytes2: &[u8] = bytemuck::cast_slice(&ws2.label[..]);
1503 assert!(bytes2.starts_with(b"bye\0"), "set_label did not write label");
1504
1505 let wg = WithGet { title: [0; 16] }.with_title("foo");
1507 assert_eq!(wg.title(), "foo");
1508 }
1509}