@@ -34,6 +34,33 @@ pub enum RclrsError {
3434 AlreadyAddedToWaitSet ,
3535}
3636
37+ impl RclrsError {
38+ /// Returns true if the error was due to a timeout, otherwise returns false.
39+ pub fn is_timeout ( & self ) -> bool {
40+ matches ! (
41+ self ,
42+ RclrsError :: RclError {
43+ code: RclReturnCode :: Timeout ,
44+ ..
45+ }
46+ )
47+ }
48+
49+ /// Returns true if the error was because a subscription, service, or client
50+ /// take failed, otherwise returns false.
51+ pub fn is_take_failed ( & self ) -> bool {
52+ matches ! (
53+ self ,
54+ RclrsError :: RclError {
55+ code: RclReturnCode :: SubscriptionTakeFailed
56+ | RclReturnCode :: ServiceTakeFailed
57+ | RclReturnCode :: ClientTakeFailed ,
58+ ..
59+ }
60+ )
61+ }
62+ }
63+
3764impl Display for RclrsError {
3865 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
3966 match self {
@@ -355,27 +382,77 @@ impl ToResult for rcl_ret_t {
355382
356383/// A helper trait to disregard timeouts as not an error.
357384pub trait RclrsErrorFilter {
385+ /// Get the first error available, or Ok(()) if there are no errors.
386+ fn first_error ( self ) -> Result < ( ) , RclrsError > ;
387+
358388 /// If the result was a timeout error, change it to `Ok(())`.
359- fn timeout_ok ( self ) -> Result < ( ) , RclrsError > ;
389+ fn timeout_ok ( self ) -> Self ;
390+
391+ /// If a subscription, service, or client take failed, change the result
392+ /// to be `Ok(())`.
393+ fn take_failed_ok ( self ) -> Self ;
394+
395+ /// Some error types just indicate an early termination but do not indicate
396+ /// that anything in the system has misbehaved. This filters out anything
397+ /// that is part of the normal operation of rcl.
398+ fn ignore_non_errors ( self ) -> Self
399+ where
400+ Self : Sized ,
401+ {
402+ self . timeout_ok ( ) . take_failed_ok ( )
403+ }
360404}
361405
362406impl RclrsErrorFilter for Result < ( ) , RclrsError > {
407+ fn first_error ( self ) -> Result < ( ) , RclrsError > {
408+ self
409+ }
410+
363411 fn timeout_ok ( self ) -> Result < ( ) , RclrsError > {
364412 match self {
365413 Ok ( ( ) ) => Ok ( ( ) ) ,
366414 Err ( err) => {
367- if matches ! (
368- err,
369- RclrsError :: RclError {
370- code: RclReturnCode :: Timeout ,
371- ..
372- }
373- ) {
374- return Ok ( ( ) ) ;
415+ if err. is_timeout ( ) {
416+ Ok ( ( ) )
417+ } else {
418+ Err ( err)
375419 }
420+ }
421+ }
422+ }
376423
377- Err ( err)
424+ fn take_failed_ok ( self ) -> Result < ( ) , RclrsError > {
425+ match self {
426+ Err ( err) => {
427+ if err. is_take_failed ( ) {
428+ // Spurious wakeup - this may happen even when a waitset indicated that
429+ // work was ready, so we won't report it as an error
430+ Ok ( ( ) )
431+ } else {
432+ Err ( err)
433+ }
378434 }
435+ other => other,
436+ }
437+ }
438+ }
439+
440+ impl RclrsErrorFilter for Vec < RclrsError > {
441+ fn first_error ( mut self ) -> Result < ( ) , RclrsError > {
442+ if self . is_empty ( ) {
443+ return Ok ( ( ) ) ;
379444 }
445+
446+ Err ( self . remove ( 0 ) )
447+ }
448+
449+ fn timeout_ok ( mut self ) -> Self {
450+ self . retain ( |err| !err. is_timeout ( ) ) ;
451+ self
452+ }
453+
454+ fn take_failed_ok ( mut self ) -> Self {
455+ self . retain ( |err| !err. is_take_failed ( ) ) ;
456+ self
380457 }
381458}
0 commit comments