Skip to main content

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}