@@ -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,39 @@ 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 Some ( events) = self . world ( ) . get_resource :: < Events < AppExit > > ( ) else {
875+ return None ;
876+ } ;
877+
878+ let mut events = reader. read ( events) ;
879+
880+ if events. len ( ) != 0 {
881+ return Some (
882+ events
883+ . find ( |exit| exit. is_error ( ) )
884+ . cloned ( )
885+ . unwrap_or ( AppExit :: Success ) ,
886+ ) ;
887+ }
888+
889+ None
890+ }
855891}
856892
857893type RunnerFn = Box < dyn FnOnce ( App ) -> AppExit > ;
@@ -870,9 +906,9 @@ fn run_once(mut app: App) -> AppExit {
870906 if let Some ( app_exit_events) = app. world ( ) . get_resource :: < Events < AppExit > > ( ) {
871907 if exit_code_reader
872908 . read ( app_exit_events)
873- . any ( |exit| * exit == AppExit :: Error )
909+ . any ( AppExit :: is_error )
874910 {
875- return AppExit :: Error ;
911+ return AppExit :: error ( ) ;
876912 }
877913 }
878914
@@ -890,21 +926,62 @@ pub enum AppExit {
890926 #[ default]
891927 Success ,
892928 /// The [`App`] experienced an unhandleable error.
893- Error ,
929+ /// Holds the exit code we expect our app to return.
930+ Error ( NonZeroU8 ) ,
931+ }
932+
933+ impl AppExit {
934+ /// Creates a [`AppExit::Error`] with a error code of 1.
935+ #[ must_use]
936+ pub fn error ( ) -> Self {
937+ Self :: Error ( NonZeroU8 :: MIN )
938+ }
939+
940+ /// Returns `true` if `self` is a [`AppExit::Success`].
941+ #[ must_use]
942+ pub fn is_success ( & self ) -> bool {
943+ matches ! ( self , AppExit :: Success )
944+ }
945+
946+ /// Returns `true` if `self` is a [`AppExit::Error`].
947+ #[ must_use]
948+ pub fn is_error ( & self ) -> bool {
949+ matches ! ( self , AppExit :: Error ( _) )
950+ }
951+
952+ /// Creates a [`AppExit`] from a code.
953+ ///
954+ /// When code is 0 a [`AppExit::Success`] is constructed otherwise a
955+ /// [`AppExit::Error`] is constructed.
956+ #[ must_use]
957+ pub fn from_code ( code : u8 ) -> Self {
958+ match NonZeroU8 :: new ( code) {
959+ Some ( code) => Self :: Error ( code) ,
960+ None => Self :: Success ,
961+ }
962+ }
963+ }
964+
965+ impl From < u8 > for AppExit {
966+ #[ must_use]
967+ fn from ( value : u8 ) -> Self {
968+ Self :: from_code ( value)
969+ }
894970}
895971
896972impl Termination for AppExit {
897973 fn report ( self ) -> std:: process:: ExitCode {
898974 match self {
899975 AppExit :: Success => ExitCode :: SUCCESS ,
900- AppExit :: Error => ExitCode :: FAILURE ,
976+ // We leave logging an error to our users
977+ AppExit :: Error ( value) => ExitCode :: from ( value. get ( ) ) ,
901978 }
902979 }
903980}
904981
905982#[ cfg( test) ]
906983mod tests {
907- use std:: marker:: PhantomData ;
984+ use std:: { marker:: PhantomData , mem } ;
908985
909986 use bevy_ecs:: {
910987 schedule:: { OnEnter , States } ,
@@ -1112,7 +1189,7 @@ mod tests {
11121189 /// fix: <https://github.com/bevyengine/bevy/pull/10389>
11131190 #[ test]
11141191 fn regression_test_10385 ( ) {
1115- use super :: { AppExit , Res , Resource } ;
1192+ use super :: { Res , Resource } ;
11161193 use crate :: PreUpdate ;
11171194
11181195 #[ derive( Resource ) ]
@@ -1139,4 +1216,11 @@ mod tests {
11391216 . add_systems ( PreUpdate , my_system)
11401217 . run ( ) ;
11411218 }
1219+
1220+ #[ test]
1221+ fn app_exit_size ( ) {
1222+ // There wont be many of them so the size isn't a issue but
1223+ // it's nice they're so small let's keep it that way.
1224+ assert_eq ! ( mem:: size_of:: <AppExit >( ) , mem:: size_of:: <u8 >( ) ) ;
1225+ }
11421226}
0 commit comments