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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::error::Error;

use crates::serde_json::Value;

use director::Director;

/// Results from an event.
#[derive(Debug)]
pub enum HandlerResult {
    /// The event was accepted and acted upon.
    Accept,
    /// The event was deferred until a later time for the given reason.
    Defer(String),
    /// The event was rejected for the given reason.
    Reject(String),
    /// The event failed with the given error.
    Fail(Box<dyn Error + Send + 'static>),
    /// The director should be restarted.
    Restart,
    /// The event was the last one which should be processed.
    Done,
}

impl HandlerResult {
    /// Create an accept result.
    pub fn accept() -> Self {
        HandlerResult::Accept
    }

    /// Create a deferral result.
    pub fn defer<M>(msg: M) -> Self
    where
        M: Into<String>,
    {
        HandlerResult::Defer(msg.into())
    }

    /// Create a rejecting result.
    pub fn reject<M>(msg: M) -> Self
    where
        M: Into<String>,
    {
        HandlerResult::Reject(msg.into())
    }

    /// Create a failure.
    pub fn fail<E>(err: E) -> Self
    where
        E: Into<Box<dyn Error + Send + Sync + 'static>>,
    {
        HandlerResult::Fail(err.into())
    }

    /// Create a restart result.
    pub fn restart() -> Self {
        HandlerResult::Restart
    }

    /// Create a completion result.
    pub fn done() -> Self {
        HandlerResult::Done
    }

    /// Combine two handler results into one.
    pub fn combine(self, other: Self) -> Self {
        match (self, other) {
            // Acceptance defers to the other.
            (HandlerResult::Accept, next) | (next, HandlerResult::Accept) => next,
            // Completion overrides any other status.
            (HandlerResult::Done, _) | (_, HandlerResult::Done) => HandlerResult::Done,
            // Once completion is handled, restart takes precedence.
            (HandlerResult::Restart, _) | (_, HandlerResult::Restart) => HandlerResult::Restart,
            // Deferring is next.
            (HandlerResult::Defer(left), HandlerResult::Defer(right)) => {
                HandlerResult::Defer(format!("{}\n{}", left, right))
            },
            (defer @ HandlerResult::Defer(_), _) | (_, defer @ HandlerResult::Defer(_)) => defer,
            // Failures are handled next.
            (fail @ HandlerResult::Fail(_), _) | (_, fail @ HandlerResult::Fail(_)) => fail,
            // All we have left are rejections; combine their messages.
            (HandlerResult::Reject(left), HandlerResult::Reject(right)) => {
                HandlerResult::Reject(format!("{}\n{}", left, right))
            },
        }
    }
}

/// Interface for handling events.
pub trait Handler {
    /// Adds the handler to a director.
    fn add_to_director<'a>(&'a self, director: &mut Director<'a>) -> Result<(), Box<dyn Error + Send + Sync>>;

    /// The JSON object is passed in and acted upon.
    fn handle(&self, kind: &str, object: &Value) -> Result<HandlerResult, Box<dyn Error + Send + Sync>>;

    /// The retry limit for a job kind.
    fn retry_limit(&self, _kind: &str) -> usize {
        5
    }

    /// The JSON object which has been retried is passed in and acted upon.
    fn handle_retry(
        &self,
        kind: &str,
        object: &Value,
        reasons: Vec<String>,
    ) -> Result<HandlerResult, Box<dyn Error + Send + Sync>> {
        if reasons.len() > self.retry_limit(kind) {
            return Ok(HandlerResult::Reject(format!(
                "retry limit ({}) reached for {}",
                reasons.len(),
                kind,
            )));
        }

        self.handle(kind, object)
    }
}