lemon_bugsnag_rs 0.1.2

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

use super::{enums::RuntimeType, StackTrace};

/// Struct corresponding to the `events -> threads` key in the BugSnag
/// error-reporting API payload. A list of background threads. Recommendeded
/// field to be added for apps that heavily rely on threading
///
/// ```
/// use lemon_bugsnag_rs::error::payload::events::Thread;
///
/// let thread = Thread::default();
/// ```
#[derive(Clone, Debug, Default, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Thread {
    /// Identifier of thread
    pub id: Option<String>,
    /// Name of thread
    pub name: Option<String>,
    /// If error was reported from this thread (either from an unhandled error
    /// or a call to bugsnag.notify), set this to true.
    pub error_reporting_thread: Option<bool>,
    /// An array of stacktrace objects. Each object represents one line in the
    /// stacktrace of the thread at the point that the error occurred.
    pub stack_trace: Option<Vec<StackTrace>>,
    /// Thread of state at time of error
    pub state: Option<String>,
    /// The runtime the thread was running on
    #[serde(rename(serialize = "type"))]
    pub runtime_type: Option<RuntimeType>,
}

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

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

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

/// Builder used to generate a [Thread] struct.
///
/// ```
/// use lemon_bugsnag_rs::error::payload::events::Thread;
///
/// // Generally, you get the builder this way, rather than creating an
/// // ThreadBuilder struct manually.
/// let mut sb = Thread::builder();
/// ```
#[cfg_attr(docsrs, doc(cfg(feature = "optional-builders")))]
#[cfg(feature = "optional-builders")]
#[derive(Default)]
pub struct ThreadBuilder {
    /// The thread
    thread: Thread,
}

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

        self
    }

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

        self
    }

    /// Set the error_reporting_thread field of the Thread struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Thread;
    ///
    /// let mut tb = Thread::builder();
    ///
    /// // Give the error_reporting_thread a value.
    /// tb.set_error_reporting_thread(false);
    ///
    /// // Unset the error_reporting_thread.
    /// tb.set_error_reporting_thread(None);
    /// ```
    pub fn set_error_reporting_thread(
        &mut self,
        error_reporting_thread: impl Into<Option<bool>>,
    ) -> &mut Self {
        self.thread.error_reporting_thread =
            error_reporting_thread.into().map(|item| item.into());

        self
    }

    /// Set the stack_trace field of the Thread struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::{
    ///     thread::Thread,
    ///     StackTrace
    /// };
    ///
    /// let mut tb = Thread::builder();
    /// let stack_trace = StackTrace::default();
    ///
    /// // Give the stack_trace a value.
    /// tb.set_stack_trace(stack_trace);
    ///
    /// // With the convenience-intos feature disabled
    /// // tb.set_stack_trace([stack_trace].to_vec());
    ///
    /// // Unset the stack_trace.
    /// tb.set_stack_trace(None);
    /// ```
    pub fn set_stack_trace(
        &mut self,
        stack_trace: impl Into<Option<Vec<StackTrace>>>,
    ) -> &mut Self {
        self.thread.stack_trace = stack_trace.into().map(|item| item.into());

        self
    }

    /// Set the state field of the Thread struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::Thread;
    ///
    /// let mut tb = Thread::builder();
    ///
    /// // Give the state a value.
    /// tb.set_state("WARN");
    ///
    /// // Unset the state.
    /// tb.set_state(None);
    /// ```
    pub fn set_state<'a>(
        &mut self,
        state: impl Into<Option<&'a str>>,
    ) -> &mut Self {
        self.thread.state = state.into().map(|item| item.into());

        self
    }

    /// Set the runtime_type field of the Thread struct.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::{
    ///     thread::Thread,
    ///     enums::RuntimeType
    /// };
    ///
    /// let mut tb = Thread::builder();
    ///
    /// // Give the runtime_type a value.
    /// tb.set_runtime_type(RuntimeType::Go);
    ///
    /// // Unset the runtime_type.
    /// tb.set_runtime_type(None);
    /// ```
    pub fn set_runtime_type(
        &mut self,
        runtime_type: impl Into<Option<RuntimeType>>,
    ) -> &mut Self {
        self.thread.runtime_type = runtime_type.into();

        self
    }

    /// Generate an [Thread] struct using the options you set using the
    /// builder.
    ///
    /// ```
    /// use lemon_bugsnag_rs::error::payload::events::{
    ///     thread::Thread,
    ///     StackTrace,
    ///     enums::RuntimeType
    /// };
    ///
    /// let mut tb = Thread::builder();
    /// let stack_trace = StackTrace::default();
    ///
    /// tb.set_id("12")
    ///     .set_name("Thread #1")
    ///     .set_error_reporting_thread(true)
    ///     .set_stack_trace(stack_trace)
    ///     .set_state("ERR")
    ///     .set_runtime_type(RuntimeType::Go);
    ///
    /// let thread = tb.build();
    /// ```
    pub fn build(&self) -> Thread {
        self.thread.clone()
    }
}

#[cfg(test)]
mod test {
    #[cfg(feature = "optional-builders")]
    #[test]
    pub fn test_error_payload_events_thread_builder() {
        use super::Thread;
        use crate::error::payload::events::enums::RuntimeType;
        use crate::error::payload::events::{StackTrace, StackTraceCode};

        let stack_trace_code: StackTraceCode = (&[
            (10 as usize, "PRINT \"HELLO WORLD\""),
            (20 as usize, "PRINT GOTO 10"),
        ][..])
            .into();

        let stack_trace = StackTrace {
            file: "Filename".to_string(),
            line_number: 12,
            column_number: 24.into(),
            method: "Function".to_string(),
            in_project: true.into(),
            code: stack_trace_code.clone().into(),
            frame_address: "Frame Address".to_string().into(),
            load_address: "Load Address".to_string().into(),
            is_lr: true.into(),
            is_pc: false.into(),
            symbol_address: "Symbol Address".to_string().into(),
            macho_file: "Macho File".to_string().into(),
            macho_load_address: "Macho Load Address".to_string().into(),
            macho_uuid: "Macho UUID".to_string().into(),
            macho_vm_address: "Macho VM Adress".to_string().into(),
            code_identifier: "Code Identifier".to_string().into(),
        };

        let test_thread = Thread {
            id: "2".to_string().into(),
            name: "Thread name".to_string().into(),
            error_reporting_thread: true.into(),
            stack_trace: vec![stack_trace].into(),
            state: "State".to_string().into(),
            runtime_type: RuntimeType::BrowserJs.into(),
        };

        let thread = Thread::builder()
            .set_id(test_thread.id.as_deref())
            .set_name(test_thread.name.as_deref())
            .set_error_reporting_thread(
                test_thread.error_reporting_thread.clone(),
            )
            .set_stack_trace(test_thread.stack_trace.clone())
            .set_state(test_thread.state.as_deref())
            .set_runtime_type(test_thread.runtime_type.clone())
            .build();

        assert_eq!(thread, test_thread);
    }
}