exceptionless 0.1.0

Rust client for Exceptionless tracking errors, log messages, and feature usage events
Documentation
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

use super::error::ErrorPayload;

pub const TYPE_ERROR: &str = "error";
pub const TYPE_LOG: &str = "log";
pub const TYPE_USAGE: &str = "usage";
pub const DATA_KEY_ERROR: &str = "@error";
pub const DATA_KEY_LEVEL: &str = "@level";
pub const DATA_KEY_USER: &str = "@user";
pub const DATA_KEY_VERSION: &str = "@version";

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Event {
    #[serde(rename = "type")]
    pub event_type: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source: Option<String>,
    pub date: DateTime<Utc>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub tags: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
    #[serde(default, skip_serializing_if = "Map::is_empty")]
    pub data: Map<String, Value>,
}

impl Event {
    pub fn new(event_type: impl Into<String>) -> Self {
        Self {
            event_type: event_type.into(),
            source: None,
            date: Utc::now(),
            tags: Vec::new(),
            message: None,
            data: Map::new(),
        }
    }

    pub fn error(error: ErrorPayload) -> Self {
        let mut event = Self::new(TYPE_ERROR);
        event.data.insert(
            DATA_KEY_ERROR.to_owned(),
            serde_json::to_value(error).unwrap_or(Value::Null),
        );
        event
    }

    pub fn log(message: impl Into<String>) -> Self {
        let mut event = Self::new(TYPE_LOG);
        event.message = Some(message.into());
        event
    }

    pub fn feature_usage(feature_name: impl Into<String>) -> Self {
        let mut event = Self::new(TYPE_USAGE);
        event.source = Some(feature_name.into());
        event
    }

    pub fn with_source(mut self, source: impl Into<String>) -> Self {
        self.source = Some(source.into());
        self
    }

    pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
        let tag = tag.into();
        if !tag.trim().is_empty() && !self.tags.contains(&tag) {
            self.tags.push(tag);
        }
        self
    }

    pub fn with_message(mut self, message: impl Into<String>) -> Self {
        self.message = Some(message.into());
        self
    }

    pub fn with_data(mut self, key: impl Into<String>, value: Value) -> Self {
        self.data.insert(key.into(), value);
        self
    }

    pub fn with_level(mut self, level: impl AsRef<str>) -> Self {
        let level = level.as_ref().trim();
        if !level.is_empty() {
            self.data
                .insert(DATA_KEY_LEVEL.to_owned(), Value::String(level.to_owned()));
        }
        self
    }

    pub fn with_user_identity(mut self, identity: impl Into<String>) -> Self {
        self.data
            .insert(DATA_KEY_USER.to_owned(), Value::String(identity.into()));
        self
    }

    pub fn with_version(mut self, version: impl Into<String>) -> Self {
        self.data
            .insert(DATA_KEY_VERSION.to_owned(), Value::String(version.into()));
        self
    }
}