Skip to main content

posthog_rs/client/
summary.rs

1//! Outcome of an immediate-delivery capture call.
2
3#[cfg(feature = "capture-v1")]
4use std::collections::HashMap;
5
6#[cfg(feature = "capture-v1")]
7use uuid::Uuid;
8
9#[cfg(feature = "capture-v1")]
10use crate::event_v1::{EventResult, EventStatus};
11
12/// The outcome of an immediate capture ([`Client::capture_immediate`] /
13/// [`Client::capture_batch_immediate`]), returned once the SDK has a terminal
14/// result for the batch — the request succeeded, or the retry budget was spent
15/// (which is an [`Err`] instead).
16///
17/// A returned `CaptureSummary` means the capture request itself succeeded (HTTP
18/// `2xx`). On the `capture-v1` pipeline the backend reports a per-event verdict,
19/// so a `2xx` can still leave some events unpersisted (`drop`/`retry`) — check
20/// [`all_persisted`](Self::all_persisted) / [`not_persisted`](Self::not_persisted)
21/// before treating the batch as fully durable. On the v0 pipeline a `2xx`
22/// persists the whole batch, so `all_persisted()` is always `true`.
23///
24/// `#[non_exhaustive]`: fields are read through accessors so more can be added
25/// without breaking callers.
26///
27/// [`Client::capture_immediate`]: crate::Client::capture_immediate
28/// [`Client::capture_batch_immediate`]: crate::Client::capture_batch_immediate
29#[derive(Debug, Clone, Default)]
30#[non_exhaustive]
31pub struct CaptureSummary {
32    submitted: usize,
33    #[cfg(feature = "capture-v1")]
34    results: HashMap<Uuid, EventResult>,
35}
36
37impl CaptureSummary {
38    /// V1 outcome: the number of events sent plus the backend's per-event verdicts.
39    #[cfg(feature = "capture-v1")]
40    pub(crate) fn from_results(submitted: usize, results: HashMap<Uuid, EventResult>) -> Self {
41        Self { submitted, results }
42    }
43
44    /// V0 outcome: a `2xx` persists the whole batch (no per-event verdicts).
45    #[cfg(not(feature = "capture-v1"))]
46    pub(crate) fn delivered(submitted: usize) -> Self {
47        Self { submitted }
48    }
49
50    /// Number of events sent on the wire (after `before_send` filtering).
51    pub fn submitted(&self) -> usize {
52        self.submitted
53    }
54
55    /// Number of submitted events the backend did not persist.
56    ///
57    /// Always `0` on the v0 pipeline (a `2xx` persists the whole batch). On
58    /// `capture-v1` this is `submitted` minus the events with an `ok`/`warning`
59    /// verdict, so it counts both `drop`/`retry` verdicts and any submitted
60    /// event the backend omitted from its response.
61    pub fn not_persisted(&self) -> usize {
62        #[cfg(feature = "capture-v1")]
63        {
64            let persisted = self
65                .results
66                .values()
67                .filter(|r| matches!(r.result, EventStatus::Ok | EventStatus::Warning))
68                .count();
69            self.submitted.saturating_sub(persisted)
70        }
71        #[cfg(not(feature = "capture-v1"))]
72        {
73            0
74        }
75    }
76
77    /// Whether every submitted event was persisted (`not_persisted() == 0`).
78    ///
79    /// Note this is **vacuously true when nothing was sent** (`submitted() == 0`),
80    /// which is what a disabled client or a fully `before_send`-filtered batch
81    /// returns. Callers that advance durable state on the strength of an immediate
82    /// delivery (e.g. committing an upstream offset) must therefore also check
83    /// `submitted()` against the number of events they intended to send — do not
84    /// gate durability on `all_persisted()` alone.
85    pub fn all_persisted(&self) -> bool {
86        self.not_persisted() == 0
87    }
88
89    /// Per-event server verdicts (`capture-v1` only). Includes persisted
90    /// (`ok`/`warning`) and unpersisted (`drop`/`retry`) verdicts; filter by
91    /// [`EventStatus`](crate::EventStatus) to isolate failures. May omit events
92    /// the backend did not report on — see [`not_persisted`](Self::not_persisted).
93    #[cfg(feature = "capture-v1")]
94    pub fn event_results(&self) -> &HashMap<Uuid, EventResult> {
95        &self.results
96    }
97}