use std::borrow::Cow;
use std::cmp;
use std::fmt;
use std::iter::FromIterator;
use std::net::{AddrParseError, IpAddr};
use std::ops;
use std::str;
use ::debugid::DebugId;
use chrono::{DateTime, Utc};
use failure::Fail;
use serde::Serializer;
use serde::{Deserialize, Serialize};
use url::Url;
use url_serde;
use uuid::Uuid;
use crate::utils::ts_seconds_float;
pub mod value {
pub use serde_json::value::{from_value, to_value, Index, Map, Number, Value};
}
pub mod map {
pub use std::collections::btree_map::{BTreeMap as Map, *};
}
pub mod debugid {
pub use debugid::{BreakpadFormat, DebugId, ParseDebugIdError};
}
pub use self::value::Value;
pub use self::map::Map;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Values<T> {
pub values: Vec<T>,
}
impl<T> Values<T> {
pub fn new() -> Values<T> {
Values { values: Vec::new() }
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
}
impl<T> Default for Values<T> {
fn default() -> Self {
Values::new()
}
}
impl<T> From<Vec<T>> for Values<T> {
fn from(values: Vec<T>) -> Self {
Values { values }
}
}
impl<T> AsRef<[T]> for Values<T> {
fn as_ref(&self) -> &[T] {
&self.values
}
}
impl<T> AsMut<Vec<T>> for Values<T> {
fn as_mut(&mut self) -> &mut Vec<T> {
&mut self.values
}
}
impl<T> ops::Deref for Values<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.values
}
}
impl<T> ops::DerefMut for Values<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.values
}
}
impl<T> FromIterator<T> for Values<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Vec::<T>::from_iter(iter).into()
}
}
impl<T> Extend<T> for Values<T> {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = T>,
{
self.values.extend(iter)
}
}
impl<'a, T> IntoIterator for &'a mut Values<T> {
type Item = <&'a mut Vec<T> as IntoIterator>::Item;
type IntoIter = <&'a mut Vec<T> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
(&mut self.values).into_iter()
}
}
impl<'a, T> IntoIterator for &'a Values<T> {
type Item = <&'a Vec<T> as IntoIterator>::Item;
type IntoIter = <&'a Vec<T> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
(&self.values).into_iter()
}
}
impl<T> IntoIterator for Values<T> {
type Item = <Vec<T> as IntoIterator>::Item;
type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct LogEntry {
pub message: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub params: Vec<Value>,
}
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct Frame {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub function: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub module: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub package: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub abs_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lineno: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub colno: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pre_context: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_line: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub post_context: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub in_app: Option<bool>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub vars: Map<String, Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image_addr: Option<Addr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub instruction_addr: Option<Addr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symbol_addr: Option<Addr>,
}
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct TemplateInfo {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub abs_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lineno: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub colno: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pre_context: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_line: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub post_context: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Stacktrace {
#[serde(default)]
pub frames: Vec<Frame>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub frames_omitted: Option<(u64, u64)>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub registers: Map<String, RegVal>,
}
impl Stacktrace {
pub fn from_frames_reversed(mut frames: Vec<Frame>) -> Option<Stacktrace> {
if frames.is_empty() {
None
} else {
frames.reverse();
Some(Stacktrace {
frames,
..Default::default()
})
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[serde(untagged)]
pub enum ThreadId {
Int(u64),
String(String),
}
impl Default for ThreadId {
fn default() -> ThreadId {
ThreadId::Int(0)
}
}
impl<'a> From<&'a str> for ThreadId {
fn from(id: &'a str) -> ThreadId {
ThreadId::String(id.to_string())
}
}
impl From<String> for ThreadId {
fn from(id: String) -> ThreadId {
ThreadId::String(id)
}
}
impl From<i64> for ThreadId {
fn from(id: i64) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl From<i32> for ThreadId {
fn from(id: i32) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl From<u32> for ThreadId {
fn from(id: u32) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl From<u16> for ThreadId {
fn from(id: u16) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl fmt::Display for ThreadId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ThreadId::Int(i) => write!(f, "{}", i),
ThreadId::String(ref s) => write!(f, "{}", s),
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct Addr(pub u64);
impl Addr {
pub fn is_null(&self) -> bool {
self.0 == 0
}
}
impl_hex_serde!(Addr, u64);
impl From<u64> for Addr {
fn from(addr: u64) -> Addr {
Addr(addr)
}
}
impl From<i32> for Addr {
fn from(addr: i32) -> Addr {
Addr(addr as u64)
}
}
impl From<u32> for Addr {
fn from(addr: u32) -> Addr {
Addr(addr as u64)
}
}
impl From<usize> for Addr {
fn from(addr: usize) -> Addr {
Addr(addr as u64)
}
}
impl<T> From<*const T> for Addr {
fn from(addr: *const T) -> Addr {
Addr(addr as u64)
}
}
impl<T> From<*mut T> for Addr {
fn from(addr: *mut T) -> Addr {
Addr(addr as u64)
}
}
impl Into<u64> for Addr {
fn into(self) -> u64 {
self.0
}
}
fn is_false(value: &bool) -> bool {
!*value
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct RegVal(pub u64);
impl_hex_serde!(RegVal, u64);
impl From<u64> for RegVal {
fn from(addr: u64) -> RegVal {
RegVal(addr)
}
}
impl From<i32> for RegVal {
fn from(addr: i32) -> RegVal {
RegVal(addr as u64)
}
}
impl From<u32> for RegVal {
fn from(addr: u32) -> RegVal {
RegVal(addr as u64)
}
}
impl From<usize> for RegVal {
fn from(addr: usize) -> RegVal {
RegVal(addr as u64)
}
}
impl<T> From<*const T> for RegVal {
fn from(addr: *const T) -> RegVal {
RegVal(addr as u64)
}
}
impl<T> From<*mut T> for RegVal {
fn from(addr: *mut T) -> RegVal {
RegVal(addr as u64)
}
}
impl Into<u64> for RegVal {
fn into(self) -> u64 {
self.0
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Thread {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<ThreadId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw_stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "is_false")]
pub crashed: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub current: bool,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct CError {
pub number: i32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
impl From<i32> for CError {
fn from(number: i32) -> CError {
CError { number, name: None }
}
}
impl Into<i32> for CError {
fn into(self) -> i32 {
self.number
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct MachException {
pub exception: i32,
pub code: u64,
pub subcode: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct PosixSignal {
pub number: i32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code_name: Option<String>,
}
impl From<i32> for PosixSignal {
fn from(number: i32) -> PosixSignal {
PosixSignal {
number,
code: None,
name: None,
code_name: None,
}
}
}
impl From<(i32, i32)> for PosixSignal {
fn from(tuple: (i32, i32)) -> PosixSignal {
let (number, code) = tuple;
PosixSignal {
number,
code: Some(code),
name: None,
code_name: None,
}
}
}
impl Into<i32> for PosixSignal {
fn into(self) -> i32 {
self.number
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct MechanismMeta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub errno: Option<CError>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signal: Option<PosixSignal>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mach_exception: Option<MachException>,
}
impl MechanismMeta {
fn is_empty(&self) -> bool {
self.errno.is_none() && self.signal.is_none() && self.mach_exception.is_none()
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Mechanism {
#[serde(rename = "type")]
pub ty: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, with = "url_serde", skip_serializing_if = "Option::is_none")]
pub help_link: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub synthetic: Option<bool>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub data: Map<String, Value>,
#[serde(default, skip_serializing_if = "MechanismMeta::is_empty")]
pub meta: MechanismMeta,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Exception {
#[serde(rename = "type")]
pub ty: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub module: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw_stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub thread_id: Option<ThreadId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mechanism: Option<Mechanism>,
}
#[derive(Debug, Fail)]
#[fail(display = "invalid level")]
pub struct ParseLevelError;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Level {
Debug,
Info,
Warning,
Error,
Fatal,
}
impl Default for Level {
fn default() -> Level {
Level::Info
}
}
impl str::FromStr for Level {
type Err = ParseLevelError;
fn from_str(string: &str) -> Result<Level, Self::Err> {
Ok(match string {
"debug" => Level::Debug,
"info" | "log" => Level::Info,
"warning" => Level::Warning,
"error" => Level::Error,
"fatal" => Level::Fatal,
_ => return Err(ParseLevelError),
})
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Level::Debug => write!(f, "debug"),
Level::Info => write!(f, "info"),
Level::Warning => write!(f, "warning"),
Level::Error => write!(f, "error"),
Level::Fatal => write!(f, "fatal"),
}
}
}
impl Level {
pub fn is_debug(&self) -> bool {
*self == Level::Debug
}
pub fn is_info(&self) -> bool {
*self == Level::Info
}
pub fn is_warning(&self) -> bool {
*self == Level::Warning
}
pub fn is_error(&self) -> bool {
*self == Level::Error
}
pub fn is_fatal(&self) -> bool {
*self == Level::Fatal
}
}
impl_str_serde!(Level);
mod breadcrumb {
use super::*;
pub fn default_timestamp() -> DateTime<Utc> {
Utc::now()
}
pub fn default_type() -> String {
"default".to_string()
}
pub fn is_default_type(ty: &str) -> bool {
ty == "default"
}
pub fn default_level() -> Level {
Level::Info
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Breadcrumb {
#[serde(default = "breadcrumb::default_timestamp", with = "ts_seconds_float")]
pub timestamp: DateTime<Utc>,
#[serde(
rename = "type",
default = "breadcrumb::default_type",
skip_serializing_if = "breadcrumb::is_default_type"
)]
pub ty: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(
default = "breadcrumb::default_level",
skip_serializing_if = "Level::is_info"
)]
pub level: Level,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub data: Map<String, Value>,
}
impl Default for Breadcrumb {
fn default() -> Breadcrumb {
Breadcrumb {
timestamp: breadcrumb::default_timestamp(),
ty: breadcrumb::default_type(),
category: Default::default(),
level: breadcrumb::default_level(),
message: Default::default(),
data: Default::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum IpAddress {
Auto,
Exact(IpAddr),
}
impl PartialEq<IpAddr> for IpAddress {
fn eq(&self, other: &IpAddr) -> bool {
match *self {
IpAddress::Auto => false,
IpAddress::Exact(ref addr) => addr == other,
}
}
}
impl cmp::PartialOrd<IpAddr> for IpAddress {
fn partial_cmp(&self, other: &IpAddr) -> Option<cmp::Ordering> {
match *self {
IpAddress::Auto => None,
IpAddress::Exact(ref addr) => addr.partial_cmp(other),
}
}
}
impl Default for IpAddress {
fn default() -> IpAddress {
IpAddress::Auto
}
}
impl fmt::Display for IpAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
IpAddress::Auto => write!(f, "{{{{auto}}}}"),
IpAddress::Exact(ref addr) => write!(f, "{}", addr),
}
}
}
impl From<IpAddr> for IpAddress {
fn from(addr: IpAddr) -> IpAddress {
IpAddress::Exact(addr)
}
}
impl str::FromStr for IpAddress {
type Err = AddrParseError;
fn from_str(string: &str) -> Result<IpAddress, AddrParseError> {
match string {
"{{auto}}" => Ok(IpAddress::Auto),
other => other.parse().map(IpAddress::Exact),
}
}
}
impl_str_serde!(IpAddress);
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct User {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ip_address: Option<IpAddress>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Request {
#[serde(default, with = "url_serde", skip_serializing_if = "Option::is_none")]
pub url: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub method: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub query_string: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cookies: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub headers: Map<String, String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub env: Map<String, String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SystemSdkInfo {
pub sdk_name: String,
pub version_major: u32,
pub version_minor: u32,
pub version_patchlevel: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum DebugImage {
Apple(AppleDebugImage),
Symbolic(SymbolicDebugImage),
Proguard(ProguardDebugImage),
}
impl DebugImage {
pub fn type_name(&self) -> &str {
match *self {
DebugImage::Apple(..) => "apple",
DebugImage::Symbolic(..) => "symbolic",
DebugImage::Proguard(..) => "proguard",
}
}
}
macro_rules! into_debug_image {
($kind:ident, $ty:ty) => {
impl From<$ty> for DebugImage {
fn from(data: $ty) -> DebugImage {
DebugImage::$kind(data)
}
}
};
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct AppleDebugImage {
pub name: String,
pub arch: Option<String>,
pub cpu_type: Option<u32>,
pub cpu_subtype: Option<u32>,
pub image_addr: Addr,
pub image_size: u64,
#[serde(default, skip_serializing_if = "Addr::is_null")]
pub image_vmaddr: Addr,
pub uuid: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SymbolicDebugImage {
pub name: String,
pub arch: Option<String>,
pub image_addr: Addr,
pub image_size: u64,
#[serde(default, skip_serializing_if = "Addr::is_null")]
pub image_vmaddr: Addr,
pub id: DebugId,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ProguardDebugImage {
pub uuid: Uuid,
}
into_debug_image!(Apple, AppleDebugImage);
into_debug_image!(Symbolic, SymbolicDebugImage);
into_debug_image!(Proguard, ProguardDebugImage);
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct DebugMeta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sdk_info: Option<SystemSdkInfo>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub images: Vec<DebugImage>,
}
impl DebugMeta {
pub fn is_empty(&self) -> bool {
self.sdk_info.is_none() && self.images.is_empty()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ClientSdkInfo {
pub name: String,
pub version: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub integrations: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub packages: Vec<ClientSdkPackage>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ClientSdkPackage {
pub name: String,
pub version: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum Context {
Device(Box<DeviceContext>),
Os(Box<OsContext>),
Runtime(Box<RuntimeContext>),
App(Box<AppContext>),
Browser(Box<BrowserContext>),
#[serde(rename = "unknown")]
Other(Map<String, Value>),
}
impl Context {
pub fn type_name(&self) -> &str {
match *self {
Context::Device(..) => "device",
Context::Os(..) => "os",
Context::Runtime(..) => "runtime",
Context::App(..) => "app",
Context::Browser(..) => "browser",
Context::Other(..) => "unknown",
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "lowercase")]
pub enum Orientation {
Portrait,
Landscape,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct DeviceContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub family: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arch: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub battery_level: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub orientation: Option<Orientation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub simulator: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub free_memory: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usable_memory: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub storage_size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub free_storage: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external_storage_size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external_free_storage: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub boot_time: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct OsContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kernel_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rooted: Option<bool>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct RuntimeContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct AppContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_start_time: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub device_app_hash: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_identifier: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_build: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct BrowserContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
macro_rules! into_context {
($kind:ident, $ty:ty) => {
impl From<$ty> for Context {
fn from(data: $ty) -> Self {
Context::$kind(Box::new(data))
}
}
};
}
into_context!(App, AppContext);
into_context!(Device, DeviceContext);
into_context!(Os, OsContext);
into_context!(Runtime, RuntimeContext);
into_context!(Browser, BrowserContext);
mod event {
use super::*;
pub fn default_id() -> Uuid {
Uuid::new_v4()
}
pub fn serialize_id<S: Serializer>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_some(&uuid.to_simple_ref().to_string())
}
pub fn default_level() -> Level {
Level::Error
}
pub fn default_platform() -> Cow<'static, str> {
Cow::Borrowed("other")
}
pub fn is_default_platform(value: &str) -> bool {
value == "other"
}
static DEFAULT_FINGERPRINT: &[Cow<'static, str>] = &[Cow::Borrowed("{{ default }}")];
pub fn default_fingerprint<'a>() -> Cow<'a, [Cow<'a, str>]> {
Cow::Borrowed(DEFAULT_FINGERPRINT)
}
#[cfg_attr(feature = "cargo-clippy", allow(ptr_arg))]
pub fn is_default_fingerprint<'a>(fp: &[Cow<'a, str>]) -> bool {
fp.len() == 1 && ((&fp)[0] == "{{ default }}" || (&fp)[0] == "{{default}}")
}
pub fn default_timestamp() -> DateTime<Utc> {
Utc::now()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Event<'a> {
#[serde(default = "event::default_id", serialize_with = "event::serialize_id")]
pub event_id: Uuid,
#[serde(
default = "event::default_level",
skip_serializing_if = "Level::is_error"
)]
pub level: Level,
#[serde(
default = "event::default_fingerprint",
skip_serializing_if = "event::is_default_fingerprint"
)]
pub fingerprint: Cow<'a, [Cow<'a, str>]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub culprit: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transaction: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logentry: Option<LogEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logger: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub modules: Map<String, String>,
#[serde(
default = "event::default_platform",
skip_serializing_if = "event::is_default_platform"
)]
pub platform: Cow<'a, str>,
#[serde(default = "event::default_timestamp", with = "ts_seconds_float")]
pub timestamp: DateTime<Utc>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub server_name: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dist: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user: Option<User>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request: Option<Request>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub contexts: Map<String, Context>,
#[serde(default, skip_serializing_if = "Values::is_empty")]
pub breadcrumbs: Values<Breadcrumb>,
#[serde(default, skip_serializing_if = "Values::is_empty")]
pub exception: Values<Exception>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub template: Option<TemplateInfo>,
#[serde(default, skip_serializing_if = "Values::is_empty")]
pub threads: Values<Thread>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub tags: Map<String, String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub extra: Map<String, Value>,
#[serde(default, skip_serializing_if = "DebugMeta::is_empty")]
pub debug_meta: Cow<'a, DebugMeta>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sdk: Option<Cow<'a, ClientSdkInfo>>,
}
impl<'a> Default for Event<'a> {
fn default() -> Self {
Event {
event_id: event::default_id(),
level: event::default_level(),
fingerprint: event::default_fingerprint(),
culprit: Default::default(),
transaction: Default::default(),
message: Default::default(),
logentry: Default::default(),
logger: Default::default(),
modules: Default::default(),
platform: event::default_platform(),
timestamp: event::default_timestamp(),
server_name: Default::default(),
release: Default::default(),
dist: Default::default(),
environment: Default::default(),
user: Default::default(),
request: Default::default(),
contexts: Default::default(),
breadcrumbs: Default::default(),
exception: Default::default(),
stacktrace: Default::default(),
template: Default::default(),
threads: Default::default(),
tags: Default::default(),
extra: Default::default(),
debug_meta: Default::default(),
sdk: Default::default(),
}
}
}
impl<'a> Event<'a> {
pub fn new() -> Event<'a> {
Default::default()
}
pub fn into_owned(self) -> Event<'static> {
Event {
event_id: self.event_id,
level: self.level,
fingerprint: Cow::Owned(
self.fingerprint
.iter()
.map(|x| Cow::Owned(x.to_string()))
.collect(),
),
culprit: self.culprit,
transaction: self.transaction,
message: self.message,
logentry: self.logentry,
logger: self.logger,
modules: self.modules,
platform: Cow::Owned(self.platform.into_owned()),
timestamp: self.timestamp,
server_name: self.server_name.map(|x| Cow::Owned(x.into_owned())),
release: self.release.map(|x| Cow::Owned(x.into_owned())),
dist: self.dist.map(|x| Cow::Owned(x.into_owned())),
environment: self.environment.map(|x| Cow::Owned(x.into_owned())),
user: self.user,
request: self.request,
contexts: self.contexts,
breadcrumbs: self.breadcrumbs,
exception: self.exception,
stacktrace: self.stacktrace,
template: self.template,
threads: self.threads,
tags: self.tags,
extra: self.extra,
debug_meta: Cow::Owned(self.debug_meta.into_owned()),
sdk: self.sdk.map(|x| Cow::Owned(x.into_owned())),
}
}
}
impl<'a> fmt::Display for Event<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Event(id: {}, ts: {})", self.event_id, self.timestamp)
}
}