lemon_bugsnag_rs 0.1.2

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

use crate::{
    common::{
        builder::{builder_options::BuilderOptions, error::BuilderError},
        cobbler::Cobbler,
        error::ErrorStack,
        traits::backend_builder_trait_sync::BackendBuilderTraitSync,
    },
    session::{
        payload::{
            device::Device, notifier::Notifier,
            session_counts::session_count::SessionCount, sessions::Session,
            App, Payload,
        },
        ureq::client::blocking::UreqSessionSync,
    },
};

/// Used to build a Ureq client for the BugSnag error-reporting API. This
/// struct is not created directly by you, the user, but rather is returned
/// by the `ureq()` function on
/// [ErrorClientBuilder](crate::error::error_client_builder::ErrorClientBuilder).
///
/// ```
/// use lemon_bugsnag_rs::common::builder::client_builder::ClientBuilder;
///
/// let mut cb = ClientBuilder::session();
/// cb.configure(
///     "api_key"
///     , "notifier_name"
///     , "notifier_version"
///     , "app_release_stage"
///     , "app_version"
/// );
///
/// // Obtain a ClientBuilderUreqSessionSync.
/// let cbuss = cb.ureq();
/// ```

pub struct ClientBuilderUreqSessionSync {
    /// The Ureq AgentBuilder, used to generate a Ureq network client.
    pub client_builder: AgentBuilder,
    pub(crate) options: BuilderOptions,
    pub(crate) api_key: Option<String>,
    pub(crate) payload_version: String,
    pub(crate) notifier: Option<Notifier>,
    pub(crate) app: Option<App>,
    pub(crate) device: Option<Device>,
    pub(crate) sessions: Option<Vec<Session>>,
    pub(crate) session_counts: Option<Vec<SessionCount>>,
}

impl BackendBuilderTraitSync<UreqSessionSync, ErrorStack>
    for ClientBuilderUreqSessionSync
{
    /// Build and return a Ureq client for the BugSnag error-reporting API.
    /// Returns a [Result] with [UreqSessionSync] as the `Ok` variant, and an
    /// [ErrorStack] as the `Err` variant.
    ///
    /// ```
    /// use lemon_bugsnag_rs::common::builder::client_builder::ClientBuilder;
    /// use lemon_bugsnag_rs::common::traits::backend_builder_trait_sync::BackendBuilderTraitSync;
    ///
    /// // Start by retreiving the builder for the BugSnag session API.
    /// let mut cb = ClientBuilder::session();
    /// // Configure the builder to suit your needs. For this example, we will
    /// // use the `configure()` helper function.
    /// cb.configure(
    ///     "Your API key goes here",
    ///     "A name to identify your application",
    ///     "Notifier version",
    ///     "dev",
    ///     "Application version",
    /// );
    ///
    /// let client = cb.ureq().build();
    /// ```
    fn build(mut self) -> Result<UreqSessionSync, ErrorStack> {
        let mut error_stack = ErrorStack::default();
        let mut missing_fields: Vec<String> = Vec::new();

        let headers = self.options.headers.as_mut().unwrap();
        if self.api_key.is_some() {
            headers.insert(
                "Bugsnag-Api-Key".to_string(),
                self.api_key.clone().unwrap(),
            );
        }

        headers.insert(
            "Bugsnag-Payload-Version".into(),
            self.payload_version.clone(),
        );

        // We check the headers and not self.api_key because it is possible for
        // the user to have directly set the API key in the headers, bypassing
        // set_api_key().
        if !headers.contains_key("Bugsnag-Api-Key") {
            missing_fields.push("api_key".to_string());
        }

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

        if missing_fields.len() > 0 {
            error_stack.errors.push(
                BuilderError::MissingField {
                    context:
                        "BackendBuilderTrait<ReqwestSessionAsync, ErrorStack>"
                            .to_string(),
                    fields: missing_fields,
                }
                .into(),
            );
        }

        if self.sessions.is_some() && self.session_counts.is_some() {
            let mut conflicting_fields: Vec<String> = Vec::new();
            conflicting_fields.push("sessions".to_string());
            conflicting_fields.push("session_counts".to_string());
            error_stack.errors.push(
                BuilderError::ConflictingField {
                    context:
                        "BackendBuilderTrait<ReqwestSessionAsync, ErrorStack>"
                            .to_string(),
                    fields: conflicting_fields,
                }
                .into(),
            );
        }

        match &self.app {
            Some(app) => {
                if app.version_code.is_some() && app.bundle_version.is_some() {
                    let mut conflicting_fields: Vec<String> = Vec::new();
                    conflicting_fields.push("app.version_code".to_string());
                    conflicting_fields.push("app.bundle_version".to_string());
                    error_stack.errors.push(
                        BuilderError::ConflictingField{
                            context: "BackendBuilderTrait<ReqwestSessionAsync, ErrorStack>".to_string(),
                            fields: conflicting_fields,
                        }
                        .into(),
                    );
                }
            }
            None => (),
        }

        if error_stack.errors.len() == 0 {
            let url = Cobbler::cobble(self.options.clone())?;
            let mut request = self.client_builder.build().post(url.as_str());
            match self.options.headers {
                Some(headers) => {
                    for (header, value) in headers.into_iter() {
                        request = request.set(header.as_str(), value.as_str());
                    }
                }
                None => (),
            };

            let payload = Payload {
                notifier: self.notifier.unwrap(),
                app: self.app,
                device: self.device,
                sessions: self.sessions,
                session_counts: self.session_counts,
            };

            Ok(UreqSessionSync { url, request, payload })
        } else {
            Err(error_stack)
        }
    }
}