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;
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,
}
}
pub fn spirit(&self) -> &Arc<Spirit<O, C>> {
&self.spirit
}
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
}
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);
}
}
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)
}
}