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}