use std::collections::BTreeMap;
use std::time::SystemTime;
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct Event<'a> {
pub name: &'static str,
pub category: EventCategory,
pub timestamp: SystemTime,
pub install_id: Option<&'a str>,
pub session_id: &'a str,
pub schema_version: u32,
pub props: &'a [Prop<'a>],
}
impl<'a> Event<'a> {
pub fn to_owned(&self) -> OwnedEvent {
OwnedEvent {
name: self.name.to_owned(),
category: self.category,
timestamp: self.timestamp,
install_id: self.install_id.map(str::to_owned),
session_id: self.session_id.to_owned(),
schema_version: self.schema_version,
props: self.props.iter().map(Prop::to_owned).collect(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum EventCategory {
Intent,
Lifecycle,
Navigation,
Census,
Custom,
}
impl EventCategory {
pub fn as_str(self) -> &'static str {
match self {
Self::Intent => "intent",
Self::Lifecycle => "lifecycle",
Self::Navigation => "navigation",
Self::Census => "census",
Self::Custom => "custom",
}
}
}
#[derive(Debug, Clone)]
pub struct Prop<'a> {
pub key: &'static str,
pub value: PropValue<'a>,
}
impl<'a> Prop<'a> {
pub fn to_owned(&self) -> OwnedProp {
OwnedProp {
key: self.key.to_owned(),
value: self.value.to_owned(),
}
}
}
#[derive(Debug, Clone)]
pub enum PropValue<'a> {
StaticStr(&'static str),
BoundedStr(&'a str),
U32(u32),
I64(i64),
F64Bucket(F64Bucket),
Bool(bool),
Enum {
variant: &'static str,
},
HistogramStrU32(&'a [(&'static str, u32)]),
}
impl<'a> PropValue<'a> {
pub fn to_owned(&self) -> OwnedPropValue {
match self {
Self::StaticStr(s) => OwnedPropValue::Str((*s).to_owned()),
Self::BoundedStr(s) => OwnedPropValue::Str((*s).to_owned()),
Self::U32(v) => OwnedPropValue::U32(*v),
Self::I64(v) => OwnedPropValue::I64(*v),
Self::F64Bucket(b) => OwnedPropValue::F64Bucket(*b),
Self::Bool(v) => OwnedPropValue::Bool(*v),
Self::Enum { variant } => OwnedPropValue::Str((*variant).to_owned()),
Self::HistogramStrU32(entries) => OwnedPropValue::HistogramStrU32(
entries.iter().map(|(k, v)| ((*k).to_owned(), *v)).collect(),
),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct F64Bucket {
pub min_x100: i64,
pub max_x100: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OwnedEvent {
pub name: String,
pub category: EventCategory,
pub timestamp: SystemTime,
pub install_id: Option<String>,
pub session_id: String,
pub schema_version: u32,
pub props: Vec<OwnedProp>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OwnedProp {
pub key: String,
pub value: OwnedPropValue,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OwnedPropValue {
Str(String),
U32(u32),
I64(i64),
F64Bucket(F64Bucket),
Bool(bool),
HistogramStrU32(Vec<(String, u32)>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum IntentSource {
Shortcut,
Menu,
Handler,
Programmatic,
Accessibility,
Unknown,
}
impl IntentSource {
pub fn as_str(self) -> &'static str {
match self {
Self::Shortcut => "shortcut",
Self::Menu => "menu",
Self::Handler => "handler",
Self::Programmatic => "programmatic",
Self::Accessibility => "accessibility",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct RemoteDataExport {
pub install_id: String,
pub fetched_at: SystemTime,
pub adapter: &'static str,
pub endpoint: String,
pub schema_version: u32,
pub events: Vec<RemoteEvent>,
pub server_metadata: BTreeMap<String, RemoteValue>,
}
#[derive(Debug, Clone, Serialize)]
pub struct RemoteEvent {
pub name: String,
pub timestamp: SystemTime,
pub properties: BTreeMap<String, RemoteValue>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum RemoteValue {
String(String),
Int(i64),
Float(f64),
Bool(bool),
Null,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn event_to_owned_round_trip() {
let props = [
Prop {
key: "name",
value: PropValue::StaticStr("app.save"),
},
Prop {
key: "count",
value: PropValue::U32(3),
},
];
let event = Event {
name: "intent.dispatched",
category: EventCategory::Intent,
timestamp: SystemTime::UNIX_EPOCH,
install_id: None,
session_id: "abc",
schema_version: 1,
props: &props,
};
let owned = event.to_owned();
assert_eq!(owned.name, "intent.dispatched");
assert_eq!(owned.props.len(), 2);
assert!(matches!(owned.props[0].value, OwnedPropValue::Str(ref s) if s == "app.save"));
assert!(matches!(owned.props[1].value, OwnedPropValue::U32(3)));
}
#[test]
fn intent_source_str() {
assert_eq!(IntentSource::Shortcut.as_str(), "shortcut");
assert_eq!(IntentSource::Unknown.as_str(), "unknown");
}
}