lemon_bugsnag_rs 0.1.2

A library for interacting with the BugSnag error-reporting and sessions APIs.
Documentation
use chrono::{DateTime, Utc};
use serde::Serialize;

#[cfg(feature = "optional-builders")]
use crate::common::builder::error::BuilderError;

/// Struct corresponding to the `events -> session` key in the BugSnag
/// error-reporting API payload. Details about web request from the client
/// experiencing the error
///
/// ```
/// use lemon_bugsnag_rs::error::payload::events::Session;
/// use chrono::Utc;
///
/// let timestamp = Utc::now();
///
/// let session = Session {
///     id: "12".into(),
///     started_at: timestamp,
///     handled: 2,
///     unhandled: 1
/// };
/// ```
#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Session {
    /// The unique session identifier
    pub id: String,
    /// The time (in ISO 8601 format) at which the session started
    pub started_at: DateTime<Utc>,
    /// The number of handled events that occurred in this session (including
    /// this event).
    pub handled: i64,
    /// The number of unhandled events that occurred in this session (including
    /// this event).
    pub unhandled: i64,
}

#[cfg_attr(docsrs, doc(cfg(feature = "optional-builders")))]
#[cfg(feature = "optional-builders")]
impl Session {
    /// Retrieve a [SessionBuilder].
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Session;
    ///
    /// let mut sb = Session::builder();
    /// ```
    pub fn builder() -> SessionBuilder {
        SessionBuilder {
            id: None,
            started_at: None,
            handled: None,
            unhandled: None,
        }
    }
}

/// Builder used to generate a [Session] struct.
///
/// ```
/// use lemon_bugsnag_rs::error::payload::events::Session;
///
/// // Generally, you get the builder this way, rather than creating an
/// // ExceptionBuilder struct manually.
/// let mut sb = Session::builder();
/// ```
#[cfg_attr(docsrs, doc(cfg(feature = "optional-builders")))]
#[cfg(feature = "optional-builders")]
pub struct SessionBuilder {
    /// The unique session identifier
    pub id: Option<String>,
    /// The time (in ISO 8601 format) at which the session started
    pub started_at: Option<DateTime<Utc>>,
    /// The number of handled events that occurred in this session (including
    /// this event).
    pub handled: Option<i64>,
    /// The number of unhandled events that occurred in this session (including
    /// this event).
    pub unhandled: Option<i64>,
}

#[cfg_attr(docsrs, doc(cfg(feature = "optional-builders")))]
#[cfg(feature = "optional-builders")]
impl SessionBuilder {
    /// Set the id field of the Session struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Session;
    /// let mut sb = Session::builder();
    ///
    /// // Give the id a value.
    /// sb.set_id("12");
    ///
    /// // Unset the id.
    /// sb.set_id(None);
    /// ```
    pub fn set_id<'a>(&mut self, id: impl Into<Option<&'a str>>) -> &mut Self {
        self.id = id.into().map(|item| item.into());

        self
    }

    /// Set the started_at field of the Session struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Session;
    /// use chrono::Utc;
    ///
    /// let timestamp = Utc::now();
    /// let mut sb = Session::builder();
    ///
    /// // Give the started_at a value.
    /// sb.set_started_at(timestamp);
    ///
    /// // Unset the started_at.
    /// sb.set_started_at(None);
    /// ```
    pub fn set_started_at<'a>(
        &mut self,
        started_at: impl Into<Option<DateTime<Utc>>>,
    ) -> &mut Self {
        self.started_at = started_at.into().map(|item| item.into());

        self
    }

    /// Set the handled field of the Session struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Session;
    /// let mut sb = Session::builder();
    ///
    /// // Give the handled a value.
    /// sb.set_handled(1);
    ///
    /// // Unset the handled.
    /// sb.set_handled(None);
    /// ```
    pub fn set_handled(
        &mut self,
        handled: impl Into<Option<i64>>,
    ) -> &mut Self {
        self.handled = handled.into();

        self
    }

    /// Set the unhandled field of the Session struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Session;
    /// let mut sb = Session::builder();
    ///
    /// // Give the unhandled a value.
    /// sb.set_unhandled(1);
    ///
    /// // Unset the unhandled.
    /// sb.set_unhandled(None);
    /// ```
    pub fn set_unhandled(
        &mut self,
        unhandled: impl Into<Option<i64>>,
    ) -> &mut Self {
        self.unhandled = unhandled.into();

        self
    }

    /// Generate an [Session] struct using the options you set using the
    /// builder.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Session;
    /// use chrono::Utc;
    ///
    /// let timestamp = Utc::now();
    /// let mut sb = Session::builder();
    ///
    /// sb.set_id("12")
    ///     .set_started_at(timestamp)
    ///     .set_handled(1)
    ///     .set_unhandled(2);
    ///
    /// let session = sb.build();
    /// ```
    pub fn build(&self) -> Result<Session, BuilderError> {
        let mut missing_fields: Vec<String> = Vec::new();

        if self.id.is_none() {
            missing_fields.push("id".to_string());
        }

        if self.started_at.is_none() {
            missing_fields.push("started_at".to_string());
        }

        if self.handled.is_none() {
            missing_fields.push("handled".to_string());
        }

        if self.unhandled.is_none() {
            missing_fields.push("unhandled".to_string());
        }

        if missing_fields.len() == 0 {
            Ok(Session {
                id: self.id.clone().unwrap(),
                started_at: self.started_at.clone().unwrap(),
                handled: self.handled.unwrap(),
                unhandled: self.unhandled.unwrap(),
            })
        } else {
            Err(BuilderError::MissingField {
                context: "Event Session Builder".to_string(),
                fields: missing_fields,
            })
        }
    }
}

#[cfg(test)]
mod test {
    #[cfg(feature = "optional-builders")]
    #[test]
    pub fn test_error_payload_event_session_builder() {
        use super::Session;
        use chrono::Timelike;

        let reference_session = Session {
            id: "1234".to_string(),
            started_at: chrono::Utc::now().with_minute(10).unwrap(),
            handled: 0,
            unhandled: 0,
        };

        let session = Session::builder()
            .set_id(reference_session.id.as_ref())
            .set_started_at(reference_session.started_at)
            .set_handled(reference_session.handled)
            .set_unhandled(reference_session.unhandled)
            .build()
            .unwrap();

        assert_eq!(session, reference_session);
    }
}