lemon_bugsnag_rs 0.1.2

A library for interacting with the BugSnag error-reporting and sessions APIs.
Documentation
use serde::Serialize;
use std::collections::HashMap;

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

use super::enums::BreadcrumbType;

/// Struct corresponding to the `events -> breadcrumb` key in the BugSnag
/// error-reporting API payload.
///
/// ```
/// use std::collections::HashMap;
/// use lemon_bugsnag_rs::error::payload::events::{
///     Breadcrumb,
///     enums::BreadcrumbType
/// };
///
/// let test_metadata: HashMap<String, String> = [(
///     "Metadata key 1".to_string(),
///     "Metadata value 1".to_string()
/// ),
/// (
///     "Metadata key 2".to_string(),
///     "Metadata value 2".to_string()
/// )].into();
///
/// let breadcrumb = Breadcrumb {
///     timestamp: "01:02:03".to_string(),
///     name: "Breadcrumb".to_string(),
///     breadcrumb_type: BreadcrumbType::Request,
///     meta_data: test_metadata.into()
/// };
/// ```
#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Breadcrumb {
    /// The time which the error event occured
    pub timestamp: String,
    /// A short description describing the error event
    pub name: String,
    /// A description categorizing the error
    #[serde(rename(serialize = "type"))]
    pub breadcrumb_type: BreadcrumbType,
    /// Additional information about the error in key/value context
    pub meta_data: Option<HashMap<String, String>>,
}

#[cfg_attr(docsrs, doc(cfg(feature = "convenience-intos")))]
#[cfg(feature = "convenience-intos")]
impl Into<Vec<Breadcrumb>> for Breadcrumb {
    fn into(self) -> Vec<Breadcrumb> {
        [self].into()
    }
}

#[cfg_attr(docsrs, doc(cfg(feature = "convenience-intos")))]
#[cfg(feature = "convenience-intos")]
impl Into<Option<Vec<Breadcrumb>>> for Breadcrumb {
    fn into(self) -> Option<Vec<Breadcrumb>> {
        Some([self].into())
    }
}

#[cfg_attr(docsrs, doc(cfg(feature = "optional-builders")))]
#[cfg(feature = "optional-builders")]
impl Breadcrumb {
    /// Retrieve a [BreadcrumbBuilder].
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Breadcrumb;
    ///
    /// let mut ab = Breadcrumb::builder();
    /// ```
    pub fn builder() -> BreadcrumbBuilder {
        BreadcrumbBuilder::default()
    }
}

/// Builder used to generate a [Breadcrumb] struct.
///
/// ```
/// use lemon_bugsnag_rs::error::payload::events::Breadcrumb;
///
/// // Generally, you get the builder this way, rather than creating an
/// // BreadcrumbBuilder struct manually.
/// let ab = Breadcrumb::builder();
/// ```
#[cfg_attr(docsrs, doc(cfg(feature = "optional-builders")))]
#[cfg(feature = "optional-builders")]
#[derive(Default)]
pub struct BreadcrumbBuilder {
    timestamp: String,
    name: String,
    breadcrumb_type: Option<BreadcrumbType>,
    meta_data: Option<HashMap<String, String>>,
}

#[cfg_attr(docsrs, doc(cfg(feature = "optional-builders")))]
#[cfg(feature = "optional-builders")]
impl BreadcrumbBuilder {
    /// Set the timestamp field of the Breadcrumb struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Breadcrumb;
    ///
    /// let mut breadcrumb_builder = Breadcrumb::builder();
    ///
    /// // Give the timestamp a value.
    /// breadcrumb_builder.set_timestamp("01:02:03");
    /// ```
    pub fn set_timestamp<'a>(
        &mut self,
        timestamp: impl Into<Option<&'a str>>,
    ) -> &mut Self {
        self.timestamp = timestamp.into().map(|item| item.into()).unwrap();

        self
    }

    /// Set the name field of the Breadcrumb struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Breadcrumb;
    ///
    /// let mut breadcrumb_builder = Breadcrumb::builder();
    ///
    /// // Give the name a value.
    /// breadcrumb_builder.set_name("SyntaxError");
    /// ```
    pub fn set_name<'a>(
        &mut self,
        name: impl Into<Option<&'a str>>,
    ) -> &mut Self {
        self.name = name.into().map(|item| item.into()).unwrap();

        self
    }

    /// Set the breadcrumb_type field of the Breadcrumb struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::{
    ///     Breadcrumb,
    ///     enums::BreadcrumbType
    /// };
    ///
    /// let mut breadcrumb_builder = Breadcrumb::builder();
    ///
    /// // Give the breadcrumb_type a value.
    /// breadcrumb_builder.set_breadcrumb_type(BreadcrumbType::Log);
    /// ```
    pub fn set_breadcrumb_type(
        &mut self,
        breadcrumb_type: BreadcrumbType,
    ) -> &mut Self {
        self.breadcrumb_type = Some(breadcrumb_type);

        self
    }

    /// Set the metadata field of the Breadcrumb struct.
    ///
    /// ```
    /// use std::collections::HashMap;
    /// use lemon_bugsnag_rs::error::payload::events::Breadcrumb;
    ///
    /// let mut breadcrumb_builder = Breadcrumb::builder();
    ///
    /// let test_metadata: HashMap<String, String> = [(
    ///     "Metadata key 1".to_string(),
    ///     "Metadata value 1".to_string()
    /// ),
    /// (
    ///     "Metadata key 2".to_string(),
    ///     "Metadata value 2".to_string()
    /// )].into();
    ///
    /// // Give the metadata a value.
    /// breadcrumb_builder.set_metadata(test_metadata.into());
    ///
    /// // Set the metadata to None.
    /// breadcrumb_builder.set_metadata(None);
    /// ```
    pub fn set_metadata(
        &mut self,
        metadata: Option<HashMap<String, String>>,
    ) -> &mut Self {
        self.meta_data = metadata;

        self
    }

    /// Generate an [Breadcrumb] struct using the options you set using the builder.
    ///
    /// ```
    /// use std::collections::HashMap;
    /// use lemon_bugsnag_rs::error::payload::events::{
    ///     Breadcrumb
    ///     , enums::BreadcrumbType
    /// };
    ///
    /// let mut breadcrumb_builder = Breadcrumb::builder();
    ///
    /// let test_metadata: HashMap<String, String> = [(
    ///     "Metadata key 1".to_string(),
    ///     "Metadata value 1".to_string()
    /// ),
    /// (
    ///     "Metadata key 2".to_string(),
    ///     "Metadata value 2".to_string()
    /// )].into();
    ///
    /// breadcrumb_builder
    ///     .set_timestamp("My doctest example Breadcrumb")
    ///     .set_name("SyntaxError")
    ///     .set_breadcrumb_type(BreadcrumbType::Log)
    ///     .set_metadata(test_metadata.into());
    ///
    /// let breadcrumb = breadcrumb_builder.build();
    /// ```
    pub fn build(&self) -> Result<Breadcrumb, BuilderError> {
        let mut missing_fields: Vec<String> = Vec::new();

        if self.timestamp == String::default() {
            missing_fields.push("timestamp".to_string());
        }
        if self.name == String::default() {
            missing_fields.push("name".to_string());
        }
        if self.breadcrumb_type.is_none() {
            missing_fields.push("breadcrumb_type".to_string());
        }

        if missing_fields.len() > 0 {
            Err(BuilderError::MissingField {
                context: "BreadcrumbBuilder".to_string(),
                fields: missing_fields,
            })
        } else {
            Ok(Breadcrumb {
                timestamp: self.timestamp.clone(),
                name: self.name.clone(),
                breadcrumb_type: self.breadcrumb_type.clone().unwrap(),
                meta_data: self.meta_data.clone(),
            })
        }
    }
}

#[cfg(test)]
mod test {
    #[cfg(feature = "optional-builders")]
    #[test]
    pub fn test_error_payload_events_breadcrumb_builder() {
        use crate::error::payload::events::{
            enums::BreadcrumbType, Breadcrumb,
        };
        use std::collections::HashMap;

        let test_metadata: HashMap<String, String> = [
            ("Metadata key 1".to_string(), "Metadata value 1".to_string()),
            ("Metadata key 2".to_string(), "Metadata value 2".to_string()),
        ]
        .into();

        let test_breadcrumb = Breadcrumb {
            timestamp: "01:02:03".to_string(),
            name: "Breadcrumb".to_string(),
            breadcrumb_type: BreadcrumbType::Request,
            meta_data: test_metadata.into(),
        };

        let mut breadcrumb_builder = Breadcrumb::builder();
        breadcrumb_builder
            .set_breadcrumb_type(test_breadcrumb.clone().breadcrumb_type)
            .set_metadata(test_breadcrumb.clone().meta_data)
            .set_name(test_breadcrumb.name.as_ref())
            .set_timestamp(test_breadcrumb.timestamp.as_ref());
        let breadcrumb = breadcrumb_builder.build().unwrap();

        assert_eq!(test_breadcrumb, breadcrumb);
    }
}