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
125
126
127
128
129
130
use thiserror::Error;

use crate::core::message::{time::Offset, Message};

#[derive(Debug, Error)]
pub enum BuilderError {
    #[error("No body provided for builder")]
    BodyNotProvided,
    #[error("Offset must be in range of -86399 and 86399 seconds")]
    OffsetOutOfBounds,
}

/// Message builder
///
/// ```
/// use spartan_lib::chrono::{Utc, FixedOffset};
/// use spartan_lib::core::message::builder::MessageBuilder;
///
/// let message = MessageBuilder::default()
///     .body("Hello, world")
///     .offset(9 * 3600)
///     .max_tries(5)
///     .timeout(60)
///     .delay(10)
///     .compose()
///     .unwrap();
/// ```
pub struct MessageBuilder {
    body: Option<Box<str>>,
    offset: i32,
    max_tries: u32,
    timeout: u32,
    delay: Option<u32>,
}

impl Default for MessageBuilder {
    fn default() -> Self {
        MessageBuilder {
            body: None,
            offset: 0,
            max_tries: 1,
            timeout: 30,
            delay: None,
        }
    }
}

impl MessageBuilder {
    /// Message body.
    #[must_use]
    pub fn body<T>(mut self, body: T) -> Self
    where
        T: Into<Box<str>>,
    {
        self.body = Some(body.into());
        self
    }

    /// Timezone offset in seconds.
    #[must_use]
    pub fn offset(mut self, offset: i32) -> Self {
        self.offset = offset;
        self
    }

    /// Max tries for message to be reserved.
    #[must_use]
    pub fn max_tries(mut self, max_tries: u32) -> Self {
        self.max_tries = max_tries;
        self
    }

    /// Message timeout. Used by GC to collect messages that execute for too long.
    #[must_use]
    pub fn timeout(mut self, timeout: u32) -> Self {
        self.timeout = timeout;
        self
    }

    /// Set message delay in seconds.
    #[must_use]
    pub fn delay(mut self, delay: u32) -> Self {
        self.delay = Some(delay);
        self
    }

    /// Compose message. Returns Err, if body was not provided.
    pub fn compose(self) -> Result<Message, BuilderError> {
        if let Some(body) = self.body {
            Ok(Message::new(
                body,
                self.delay,
                Offset::new(self.offset).ok_or(BuilderError::OffsetOutOfBounds)?,
                self.max_tries,
                self.timeout,
            ))
        } else {
            Err(BuilderError::BodyNotProvided)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::MessageBuilder;

    #[test]
    fn creates_message() {
        MessageBuilder::default()
            .body("Hello, world")
            .max_tries(3)
            .offset(100)
            .delay(1)
            .timeout(40)
            .compose()
            .unwrap();
    }

    #[test]
    #[should_panic]
    fn fails_with_empty_body() {
        MessageBuilder::default()
            .max_tries(3)
            .offset(100)
            .delay(1)
            .timeout(40)
            .compose()
            .unwrap();
    }
}