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}