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);
        }
    }
}