system_service/
application.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//! General types applicable to any Application
15use std::path::Path;
16use std::borrow::Cow;
17
18use chan::Receiver;
19use chan_signal::Signal;
20
21use logging::LogOptions;
22
23/// Indicates whether the run loop should halt
24pub enum Stopping {
25    /// The run loop should halt
26    Yes,
27
28    /// The run loop should continue
29    No
30}
31
32/// Trait required for loading Config from file
33pub trait Config {
34    /// Parse options and return it
35    ///
36    /// Implementers should use the `die!` macro to handle failure.
37    fn load<O: Options>(_: &O) -> Self;
38}
39
40/// Trait required for loading CLI options
41pub trait Options : LogOptions {
42    /// Parse options and return them
43    ///
44    /// Implementers should use the `die!` macro to handle failure.
45    fn load() -> Self;
46
47    /// Get the path to a config file
48    ///
49    /// This method is needed since Config::load is expected to be generic.
50    fn config_path<'a>(&'a self) -> Cow<'a, Path>;
51}
52
53/// A context passed to `Application::run_once`
54///
55/// Gives the application control over when to execute certain operations like
56/// signal handling.
57pub struct Context {
58    pub(crate) signal: Receiver<Signal>
59}
60
61impl Context {
62    pub fn poll_signals<A: Application>(&self, app: &mut A) {
63        let signal = &self.signal;
64
65        // Handle any and all pending signals.
66        loop {
67            chan_select! {
68                default => { break; },
69                signal.recv() -> sig => {
70                    debug!("Received signal: {:?}", sig);
71                    sig.map(|s| app.received_signal(s));
72                },
73            }
74        }
75    }
76}
77
78/// The application; domain-specific program logic
79pub trait Application: Sized {
80    /// Main error export of the Application
81    type Err: Send + 'static;
82
83    /// Config to be loaded from a file
84    type Config: Config;
85
86    /// Options from the command line
87    type Options: Options;
88
89    /// Create a new instance given the options and config
90    fn new(_: Self::Options, _: Self::Config) -> Result<Self, Self::Err>;
91
92    /// Called repeatedly in the main loop of the application.
93    fn run_once(&mut self, context: &Context) -> Result<Stopping, Self::Err>;
94
95    /// Which signal the application is interested in receiving.
96    ///
97    /// By default, only INT and TERM are blocked and handled.
98    fn signals() -> &'static [Signal] {
99        static SIGNALS: &[Signal] = &[Signal::INT, Signal::TERM];
100        SIGNALS
101    }
102
103    /// Handle a received signal
104    fn received_signal(&mut self, _: Signal) {
105        die!("received_signal default action");
106    }
107
108    /// Called when the application is shutting down
109    fn shutdown(self) -> Result<(), Self::Err> {
110        Ok(())
111    }
112}