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
// Copyright 2018 OneSignal, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A framework for writing system services
//!
//! # About
//!
//! Every application which runs as a service should implement things like
//! logging, signal handling, graceful shutdown, CLI option parsing, and
//! configuration file parsing. This package provides a semi-opinionated
//! framework for doing so.
//!
//! # Usage
//!
//! There are several traits exported here including [`Application`], [`Config`],
//! [`Options`], and [`LogOptions`]. The two options traits should be implemented
//! for your CLI option loadind, [`Config`] for your config file loading, and
//! [`Application`] for your application logic.
//!
//! The primary run method is [`Application::run_once`] which is called over and
//! over again in a loop. It is provided a [`Context`] type which gives the
//! application control of when it checks for signals. Any received signals are
//! passed to [`Application::received_signal`] for handling.
//!
//! Once [`Application::run_once`] returns [`Stopping::Yes`], the main loop
//! terminates and invokes [`Application::shutdown`] before exitting.
//!
//! [`Application`]: trait.Application.html
//! [`Application::run_once`]: trait.Application.html#tymethod.run_once
//! [`Application::received_signal`]: trait.Application.html#tymethod.received_signal
//! [`Application::shutdown`]: trait.Application.html#tymethod.shutdown
//! [`Stopping::Yes`]: enum.Stopping.html#variant.Yes
//! [`Config`]: trait.Config.html
//! [`Options`]: trait.Options.html
//! [`LogOptions`]: trait.LogOptions.html
//! [`Context`]: struct.Context.html

#[macro_use] extern crate chan;
#[macro_use] extern crate log;

extern crate chan_signal;
extern crate env_logger;

/// Print a message to stderr and exit(1)
#[macro_export]
macro_rules! die {
    ($($arg:tt)*) => {{
        eprintln!($($arg)*);
        ::std::process::exit(1);
    }}
}

mod application; // general app stuff
mod logging;

pub use application::{
    Application,
    Stopping,
    Config,
    Options,
    Context
};

pub use logging::LogOptions;

/// Run an Application
///
/// This should be called in your `fn main()` with something like the following.
///
/// ```rust
/// fn main() {
///     if let Err(err) = run::<MyApplication>() {
///         die!("Application encountered error: {}", err);
///     }
/// }
/// ```
///
/// CLI option loading, config loading, signal handling, and etc. are all
/// initialized automatically on the Application's behalf.
pub fn run<T>() -> Result<(), T::Err>
    where T: Application
{
    let signal = chan_signal::notify(T::signals());
    let context = Context { signal };

    let opts = T::Options::load();

    let _ = logging::init::<T::Options>(&opts);
    let config = Config::load(&opts);

    let mut app = T::new(opts, config)?;

    loop {
        if let Stopping::Yes = app.run_once(&context)? {
            break;
        }
    }

    app.shutdown()?;

    Ok(())
}