better_posthog/events.rs
1//! Public API for capturing PostHog events.
2
3use std::collections::HashMap;
4
5use crate::client::CLIENT;
6
7/// Captures a single event and sends it to PostHog.
8///
9/// If the client is not initialized or the queue is full, the event is dropped
10/// and a warning is logged. This function never blocks.
11///
12/// # Examples
13///
14/// ```no_run
15/// use better_posthog::{events, Event};
16///
17/// let event = Event::new("button_click", "user_123");
18/// events::capture(event);
19/// ```
20pub fn capture(event: Event) {
21 if let Some(client) = CLIENT.get() {
22 client.worker.capture(event);
23 }
24}
25
26/// Captures a batch of events and sends them to PostHog in a single request.
27///
28/// If the client is not initialized or the queue is full, the batch is dropped
29/// and a warning is logged. This function never blocks.
30///
31/// # Examples
32///
33/// ```no_run
34/// use better_posthog::{events, Event};
35///
36/// let events = vec![
37/// Event::new("page_view", "user_123"),
38/// Event::new("button_click", "user_123"),
39/// ];
40/// events::batch(events);
41/// ```
42pub fn batch(events: Vec<Event>) {
43 if let Some(client) = CLIENT.get() {
44 client.worker.batch(events);
45 }
46}
47
48/// A PostHog analytics event.
49#[derive(Debug, Clone, serde::Serialize)]
50pub struct Event {
51 /// The event name.
52 pub event: String,
53 /// The user's unique identifier.
54 pub distinct_id: String,
55 /// Custom properties attached to the event.
56 pub properties: HashMap<String, serde_json::Value>,
57 /// Optional ISO 8601 timestamp. If not set, PostHog uses server time.
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub timestamp: Option<String>,
60}
61
62impl Event {
63 /// Creates a new event with the given name and distinct ID.
64 ///
65 /// # Examples
66 ///
67 /// ```
68 /// use better_posthog::Event;
69 ///
70 /// let event = Event::new("page_view", "user_123");
71 /// ```
72 pub fn new<S: Into<String>>(event: S, distinct_id: S) -> Self {
73 Self {
74 event: event.into(),
75 distinct_id: distinct_id.into(),
76 properties: HashMap::new(),
77 timestamp: None,
78 }
79 }
80
81 /// Creates a new event with a generated UUID v7 as the distinct ID.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// use better_posthog::Event;
87 ///
88 /// let event = Event::new_anonymous("anonymous_action");
89 /// ```
90 pub fn new_anonymous<S: Into<String>>(event: S) -> Self {
91 Self {
92 event: event.into(),
93 distinct_id: uuid::Uuid::now_v7().to_string(),
94 properties: HashMap::new(),
95 timestamp: None,
96 }
97 }
98
99 /// Returns a builder for constructing an event.
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// use better_posthog::Event;
105 ///
106 /// let event = Event::builder()
107 /// .event("button_click")
108 /// .distinct_id("user_456")
109 /// .property("button_id", "submit")
110 /// .build();
111 /// ```
112 #[must_use]
113 pub fn builder() -> EventBuilder {
114 EventBuilder::default()
115 }
116
117 /// Inserts a property into the event.
118 ///
119 /// # Examples
120 ///
121 /// ```
122 /// use better_posthog::Event;
123 ///
124 /// let mut event = Event::new("purchase", "user_789");
125 /// event.insert_property("amount", 99.99);
126 /// event.insert_property("currency", "USD");
127 /// ```
128 pub fn insert_property<K, V>(&mut self, key: K, value: V)
129 where
130 K: Into<String>,
131 V: Into<serde_json::Value>,
132 {
133 self.properties.insert(key.into(), value.into());
134 }
135}
136
137/// Builder for constructing [`Event`] instances.
138#[derive(Debug, Default)]
139pub struct EventBuilder {
140 event: Option<String>,
141 distinct_id: Option<String>,
142 properties: HashMap<String, serde_json::Value>,
143 timestamp: Option<String>,
144}
145
146impl EventBuilder {
147 /// Sets the event name.
148 #[must_use]
149 pub fn event<S: Into<String>>(mut self, event: S) -> Self {
150 self.event = Some(event.into());
151 self
152 }
153
154 /// Sets the distinct ID.
155 #[must_use]
156 pub fn distinct_id<S: Into<String>>(mut self, distinct_id: S) -> Self {
157 self.distinct_id = Some(distinct_id.into());
158 self
159 }
160
161 /// Adds a property to the event.
162 #[must_use]
163 pub fn property<K: Into<String>, V: Into<serde_json::Value>>(mut self, key: K, value: V) -> Self {
164 self.properties.insert(key.into(), value.into());
165 self
166 }
167
168 /// Sets the timestamp (ISO 8601 format).
169 #[must_use]
170 pub fn timestamp<S: Into<String>>(mut self, timestamp: S) -> Self {
171 self.timestamp = Some(timestamp.into());
172 self
173 }
174
175 /// Builds the event.
176 ///
177 /// # Panics
178 ///
179 /// Panics if `event` is not set.
180 #[must_use]
181 pub fn build(self) -> Event {
182 Event {
183 event: self.event.expect("event name is required"),
184 distinct_id: self.distinct_id.unwrap_or_else(|| uuid::Uuid::now_v7().to_string()),
185 properties: self.properties,
186 timestamp: self.timestamp,
187 }
188 }
189}