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
// RGB standard library
// Written in 2020 by
//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the MIT License
// along with this software.
// If not, see <https://opensource.org/licenses/MIT>.

use std::error::Error;

/// Trait for simpler service implementation with run loops
#[async_trait]
pub trait Service {
    /// Run loop for the service, which must never return. If you have a run
    /// loop that may fail, use [`TryService`] trait istead
    async fn run_loop(self);
}

/// Trait for simpler service implementation with run loops which may fail with
/// `TryService::ErrorType` errors; otherwise they should never return
#[async_trait]
pub trait TryService: Sized {
    /// Type of the error which is produced in case of service failure and
    /// is returned from the internal [`try_run_loop()`] procedure
    type ErrorType: Error;

    /// NB: Do not reimplement this one: the function keeps in check that if the
    /// failure happens during run loop, the program will panic reporting the
    /// failure. To implement the actual run loop please provide implementation
    /// for [`try_run_loop()`]
    async fn run_or_panic(self, service_name: &str) {
        let should_not_return = self.try_run_loop().await;
        let msg = handle_failure(service_name, should_not_return);
        panic!(msg)
    }

    /// Main failable run loop implementation. Must produce an error of type
    /// [`TryService::ErrorType`] or never return.
    async fn try_run_loop(self) -> Result<(), Self::ErrorType>;
}

/// Marker trait that can be implemented for data structures used by `Clap` or
/// by any other form of API handling.
pub trait Exec {
    /// Runtime context data type, that is provided for execution context.
    type Runtime: Sized;
    /// Error type that may result from the execution
    type Error: ::std::error::Error;
    /// Main execution routine
    fn exec(&self, runtime: &mut Self::Runtime) -> Result<(), Self::Error>;
}

fn handle_failure<T>(
    service_name: &str,
    result: Result<T, impl ::std::error::Error>,
) -> String {
    match result {
        Err(err) => {
            format!("{} run loop has failed with error {}", service_name, err)
        }
        Ok(_) => {
            format!("{} has failed without reporting a error", service_name)
        }
    }
}