@@ -13,11 +13,14 @@ use bevy_ecs::{
1313#[ cfg( feature = "trace" ) ]
1414use bevy_utils:: tracing:: info_span;
1515use bevy_utils:: { tracing:: debug, HashMap } ;
16- use std:: panic:: { catch_unwind, resume_unwind, AssertUnwindSafe } ;
1716use std:: {
1817 fmt:: Debug ,
1918 process:: { ExitCode , Termination } ,
2019} ;
20+ use std:: {
21+ num:: NonZeroU8 ,
22+ panic:: { catch_unwind, resume_unwind, AssertUnwindSafe } ,
23+ } ;
2124use thiserror:: Error ;
2225
2326bevy_ecs:: define_label!(
@@ -852,6 +855,37 @@ impl App {
852855 self . main_mut ( ) . ignore_ambiguity ( schedule, a, b) ;
853856 self
854857 }
858+
859+ /// Attempts to determine if an [`AppExit`] was raised since the last update.
860+ ///
861+ /// Will attempt to return the first [`Error`](AppExit::Error) it encounters.
862+ pub fn should_exit ( & self ) -> Option < AppExit > {
863+ let mut reader = ManualEventReader :: default ( ) ;
864+
865+ self . should_exit_manual ( & mut reader)
866+ }
867+
868+ /// Several app runners in this crate keep their own [`ManualEventReader<AppExit>`].
869+ /// This exists to accommodate them.
870+ pub ( crate ) fn should_exit_manual (
871+ & self ,
872+ reader : & mut ManualEventReader < AppExit > ,
873+ ) -> Option < AppExit > {
874+ let events = self . world ( ) . get_resource :: < Events < AppExit > > ( ) ?;
875+
876+ let mut events = reader. read ( events) ;
877+
878+ if events. len ( ) != 0 {
879+ return Some (
880+ events
881+ . find ( |exit| exit. is_error ( ) )
882+ . cloned ( )
883+ . unwrap_or ( AppExit :: Success ) ,
884+ ) ;
885+ }
886+
887+ None
888+ }
855889}
856890
857891type RunnerFn = Box < dyn FnOnce ( App ) -> AppExit > ;
@@ -870,9 +904,9 @@ fn run_once(mut app: App) -> AppExit {
870904 if let Some ( app_exit_events) = app. world ( ) . get_resource :: < Events < AppExit > > ( ) {
871905 if exit_code_reader
872906 . read ( app_exit_events)
873- . any ( |exit| * exit == AppExit :: Error )
907+ . any ( AppExit :: is_error )
874908 {
875- return AppExit :: Error ;
909+ return AppExit :: error ( ) ;
876910 }
877911 }
878912
@@ -890,21 +924,62 @@ pub enum AppExit {
890924 #[ default]
891925 Success ,
892926 /// The [`App`] experienced an unhandleable error.
893- Error ,
927+ /// Holds the exit code we expect our app to return.
928+ Error ( NonZeroU8 ) ,
929+ }
930+
931+ impl AppExit {
932+ /// Creates a [`AppExit::Error`] with a error code of 1.
933+ #[ must_use]
934+ pub fn error ( ) -> Self {
935+ Self :: Error ( NonZeroU8 :: MIN )
936+ }
937+
938+ /// Returns `true` if `self` is a [`AppExit::Success`].
939+ #[ must_use]
940+ pub fn is_success ( & self ) -> bool {
941+ matches ! ( self , AppExit :: Success )
942+ }
943+
944+ /// Returns `true` if `self` is a [`AppExit::Error`].
945+ #[ must_use]
946+ pub fn is_error ( & self ) -> bool {
947+ matches ! ( self , AppExit :: Error ( _) )
948+ }
949+
950+ /// Creates a [`AppExit`] from a code.
951+ ///
952+ /// When code is 0 a [`AppExit::Success`] is constructed otherwise a
953+ /// [`AppExit::Error`] is constructed.
954+ #[ must_use]
955+ pub fn from_code ( code : u8 ) -> Self {
956+ match NonZeroU8 :: new ( code) {
957+ Some ( code) => Self :: Error ( code) ,
958+ None => Self :: Success ,
959+ }
960+ }
961+ }
962+
963+ impl From < u8 > for AppExit {
964+ #[ must_use]
965+ fn from ( value : u8 ) -> Self {
966+ Self :: from_code ( value)
967+ }
894968}
895969
896970impl Termination for AppExit {
897971 fn report ( self ) -> std:: process:: ExitCode {
898972 match self {
899973 AppExit :: Success => ExitCode :: SUCCESS ,
900- AppExit :: Error => ExitCode :: FAILURE ,
974+ // We leave logging an error to our users
975+ AppExit :: Error ( value) => ExitCode :: from ( value. get ( ) ) ,
901976 }
902977 }
903978}
904979
905980#[ cfg( test) ]
906981mod tests {
907- use std:: marker:: PhantomData ;
982+ use std:: { marker:: PhantomData , mem } ;
908983
909984 use bevy_ecs:: {
910985 schedule:: { OnEnter , States } ,
@@ -1112,7 +1187,7 @@ mod tests {
11121187 /// fix: <https://github.com/bevyengine/bevy/pull/10389>
11131188 #[ test]
11141189 fn regression_test_10385 ( ) {
1115- use super :: { AppExit , Res , Resource } ;
1190+ use super :: { Res , Resource } ;
11161191 use crate :: PreUpdate ;
11171192
11181193 #[ derive( Resource ) ]
@@ -1139,4 +1214,11 @@ mod tests {
11391214 . add_systems ( PreUpdate , my_system)
11401215 . run ( ) ;
11411216 }
1217+
1218+ #[ test]
1219+ fn app_exit_size ( ) {
1220+ // There wont be many of them so the size isn't a issue but
1221+ // it's nice they're so small let's keep it that way.
1222+ assert_eq ! ( mem:: size_of:: <AppExit >( ) , mem:: size_of:: <u8 >( ) ) ;
1223+ }
11421224}
0 commit comments