1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
//! The running application part. //! //! The convenient way to manage the application runtime is through //! [`Builder::run`][crate::SpiritBuilder::run]. If more flexibility is needed, the //! [`Builder::build`][crate::SpiritBuilder::build] can be used instead. That method returns the //! [`App`][crate::app::App] object, representing the application runner. The application can then //! be run at any later time, as convenient. use std::process; use std::sync::Arc; use log::debug; use serde::de::DeserializeOwned; use structopt::StructOpt; use crate::bodies::{InnerBody, WrapBody}; use crate::error; use crate::spirit::Spirit; use crate::AnyError; /// The running application part. /// /// This is returned by [`Builder::build`][crate::SpiritBuilder::build] and represents the rest of /// the application runtime except the actual application body. It can be used to run at any later /// time, after the spirit has been created. /// /// This carries all the [around-bodies][crate::Extensible::run_around] and /// [before-bodies][crate::Extensible::run_before]. If you run the application body directly, not /// through this, some of the pipelines or extensions might not work as expected. /// /// The [`Builder::run`][crate::SpiritBuilder::run] is just a convenient wrapper around this. Note /// that that one handles and logs errors from the application startup as well as from its runtime. /// Here it is up to the caller to handle the startup errors. /// /// # Examples /// /// ```rust /// use spirit::{AnyError, Empty, Spirit}; /// use spirit::prelude::*; /// /// # fn main() -> Result<(), AnyError> { /// Spirit::<Empty, Empty>::new() /// .build(true)? /// .run_term(|| { /// println!("Hello world"); /// Ok(()) /// }); /// # Ok(()) /// # } /// ``` pub struct App<O, C> { spirit: Arc<Spirit<O, C>>, inner: InnerBody, wrapper: WrapBody, } impl<O, C> App<O, C> where O: StructOpt + Send + Sync + 'static, C: DeserializeOwned + Send + Sync + 'static, { pub(crate) fn new(spirit: Arc<Spirit<O, C>>, inner: InnerBody, wrapper: WrapBody) -> Self { Self { spirit, inner, wrapper, } } /// Access to the built spirit object. /// /// The object can be used to manipulate the runtime of the application, access the current /// configuration and register further callbacks (and extensions and pipelines). /// /// Depending on your needs, you may pass it to the closure started with [`run`][App::run] or /// even placed into some kind of global storage. pub fn spirit(&self) -> &Arc<Spirit<O, C>> { &self.spirit } /// Run the application with provided body. /// /// This will run the provided body. However, it'll wrap it in all the /// [around-bodies][crate::Extensible::run_around] and precede it with all the /// [before-bodies][crate::Extensible::run_before]. If any of these fail, or if the `body` /// fails, the error is propagated (and further bodies are not started). /// /// Furthermore, depending on the [`autojoin_bg_thread`][crate::Extensible::autojoin_bg_thread] /// configuration, termination and joining of the background thread may be performed. If the /// body errors, termination is done unconditionally (which may be needed in some corner cases /// to not deadlock on error). /// /// In other words, unless you have very special needs, this is how you actually invoke the /// application itself. /// /// Any errors are simply returned and it is up to the caller to handle them somehow. pub fn run<B>(self, body: B) -> Result<(), AnyError> where B: FnOnce() -> Result<(), AnyError> + Send + 'static, { debug!("Running bodies"); let inner = self.inner; let inner = move || inner().and_then(|()| body()); let result = (self.wrapper)(Box::new(inner)); if result.is_err() { self.spirit.terminate(); } self.spirit.maybe_autojoin_bg_thread(); result } /// Similar to [`run`][App::run], but with error handling. /// /// This calls the [`run`][App::run]. However, if there are any errors, they are logged and the /// application terminates with non-zero exit code. pub fn run_term<B>(self, body: B) where B: FnOnce() -> Result<(), AnyError> + Send + 'static, { if error::log_errors("top-level", || self.run(body)).is_err() { process::exit(1); } } }