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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! 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 std::thread;

use log::debug;
use serde::de::DeserializeOwned;
use structopt::StructOpt;

use crate::bodies::{InnerBody, WrapBody};
use crate::error;
use crate::spirit::Spirit;
use crate::terminate_guard::TerminateGuard;
use crate::utils::FlushGuard;
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 _flush = FlushGuard;
        struct ScopeGuard<F: FnOnce()>(Option<F>);
        impl<F: FnOnce()> Drop for ScopeGuard<F> {
            fn drop(&mut self) {
                self.0.take().expect("Drop called twice")();
            }
        }
        let spirit = &self.spirit;
        let _thread = ScopeGuard(Some(|| {
            if thread::panicking() {
                spirit.terminate();
            }
            spirit.maybe_autojoin_bg_thread();
        }));
        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();
        }
        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,
    {
        let flush = FlushGuard;
        if error::log_errors("top-level", || self.run(body)).is_err() {
            drop(flush);
            process::exit(1);
        }
    }

    /// Run the application in a background thread for testing purposes.
    ///
    /// This'll run the application and return an RAII guard. That guard can be used to access the
    /// [Spirit] and manipulate it. It also terminates the application and background thread when
    /// dropped.
    ///
    /// This is for testing purposes (it panics if there are errors). See the [testing guide].
    ///
    /// testing guide: crate::guide::testing
    pub fn run_test<B>(self, body: B) -> TerminateGuard<O, C>
    where
        B: FnOnce() -> Result<(), AnyError> + Send + 'static,
    {
        let spirit = Arc::clone(self.spirit());
        let bg_thread = thread::spawn(move || self.run(body));
        TerminateGuard::new(spirit, bg_thread)
    }
}