system_service/
lib.rs

1// Copyright 2018 OneSignal, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// 	http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A framework for writing system services
16//!
17//! # About
18//!
19//! Every application which runs as a service should implement things like
20//! logging, signal handling, graceful shutdown, CLI option parsing, and
21//! configuration file parsing. This package provides a semi-opinionated
22//! framework for doing so.
23//!
24//! # Usage
25//!
26//! There are several traits exported here including [`Application`], [`Config`],
27//! [`Options`], and [`LogOptions`]. The two options traits should be implemented
28//! for your CLI option loadind, [`Config`] for your config file loading, and
29//! [`Application`] for your application logic.
30//!
31//! The primary run method is [`Application::run_once`] which is called over and
32//! over again in a loop. It is provided a [`Context`] type which gives the
33//! application control of when it checks for signals. Any received signals are
34//! passed to [`Application::received_signal`] for handling.
35//!
36//! Once [`Application::run_once`] returns [`Stopping::Yes`], the main loop
37//! terminates and invokes [`Application::shutdown`] before exitting.
38//!
39//! [`Application`]: trait.Application.html
40//! [`Application::run_once`]: trait.Application.html#tymethod.run_once
41//! [`Application::received_signal`]: trait.Application.html#tymethod.received_signal
42//! [`Application::shutdown`]: trait.Application.html#tymethod.shutdown
43//! [`Stopping::Yes`]: enum.Stopping.html#variant.Yes
44//! [`Config`]: trait.Config.html
45//! [`Options`]: trait.Options.html
46//! [`LogOptions`]: trait.LogOptions.html
47//! [`Context`]: struct.Context.html
48
49#[macro_use] extern crate chan;
50#[macro_use] extern crate log;
51
52extern crate chan_signal;
53extern crate env_logger;
54
55/// Print a message to stderr and exit(1)
56#[macro_export]
57macro_rules! die {
58    ($($arg:tt)*) => {{
59        eprintln!($($arg)*);
60        ::std::process::exit(1);
61    }}
62}
63
64mod application; // general app stuff
65mod logging;
66
67pub use application::{
68    Application,
69    Stopping,
70    Config,
71    Options,
72    Context
73};
74
75pub use logging::LogOptions;
76
77/// Run an Application
78///
79/// This should be called in your `fn main()` with something like the following.
80///
81/// ```rust
82/// fn main() {
83///     if let Err(err) = run::<MyApplication>() {
84///         die!("Application encountered error: {}", err);
85///     }
86/// }
87/// ```
88///
89/// CLI option loading, config loading, signal handling, and etc. are all
90/// initialized automatically on the Application's behalf.
91pub fn run<T>() -> Result<(), T::Err>
92    where T: Application
93{
94    let signal = chan_signal::notify(T::signals());
95    let context = Context { signal };
96
97    let opts = T::Options::load();
98
99    let _ = logging::init::<T::Options>(&opts);
100    let config = Config::load(&opts);
101
102    let mut app = T::new(opts, config)?;
103
104    loop {
105        if let Stopping::Yes = app.run_once(&context)? {
106            break;
107        }
108    }
109
110    app.shutdown()?;
111
112    Ok(())
113}