tiny_counter/
lib.rs

1//! Track event counts across multiple time windows with fixed memory and fast queries.
2//!
3//! This library solves the problem of tracking events (like user actions, API calls, or system
4//! events) when you need to answer questions like "how many times did this happen in the last
5//! week?" without storing every individual timestamp.
6//!
7//! # Key Features
8//!
9//! - **Fixed memory**: Each event uses ~2KB regardless of event count
10//! - **Multiple time windows**: Query at different granularities (minutes, hours, days, weeks, months, years)
11//! - **Fast queries**: All queries run in O(buckets) time, independent of event count
12//! - **Persistence**: Save to SQLite, JSON, or custom storage backends
13//! - **Merge-friendly**: Combine event data from multiple sources for distributed systems
14//!
15//! # Quick Start
16//!
17//! ```
18//! use tiny_counter::EventStore;
19//! use chrono::Duration;
20//!
21//! let mut store = EventStore::new();
22//!
23//! // Record events
24//! store.record("app_launch");
25//! store.record("app_launch");
26//!
27//! // Query last 7 days
28//! let launches = store
29//!     .query("app_launch")
30//!     .last_days(7)
31//!     .sum()
32//!     .unwrap_or(0);
33//!
34//! assert_eq!(launches, 2);
35//! ```
36//!
37//! # Recording Events
38//!
39//! Record events that just happened:
40//!
41//! ```
42//! # use tiny_counter::EventStore;
43//! let mut store = EventStore::new();
44//! store.record("button_click");
45//! store.record_count("api_call", 5);
46//! ```
47//!
48//! Record events from the past:
49//!
50//! ```
51//! # use tiny_counter::EventStore;
52//! # use chrono::{Duration, Utc};
53//! let mut store = EventStore::new();
54//! let timestamp = Utc::now() - Duration::days(2);
55//! store.record_at("feature_used", timestamp).unwrap();
56//! store.record_ago("sync", Duration::hours(3));
57//! ```
58//!
59//! # Querying Events
60//!
61//! Query event counts across different time ranges:
62//!
63//! ```
64//! # use tiny_counter::EventStore;
65//! let mut store = EventStore::new();
66//! store.record("app_launch");
67//!
68//! // Different time granularities
69//! let last_hour = store.query("app_launch").last_minutes(60).sum().unwrap();
70//! let last_day = store.query("app_launch").last_hours(24).sum().unwrap();
71//! let last_week = store.query("app_launch").last_days(7).sum().unwrap();
72//! ```
73//!
74//! # Rate Limiting
75//!
76//! Enforce rate limits with multiple constraints:
77//!
78//! ```
79//! # use tiny_counter::{EventStore, TimeUnit};
80//! let mut store = EventStore::new();
81//!
82//! let result = store
83//!     .limit()
84//!     .at_most("api_call", 10, TimeUnit::Minutes)
85//!     .at_most("api_call", 100, TimeUnit::Hours)
86//!     .check_and_record("api_call");
87//!
88//! assert!(result.is_ok());
89//! ```
90//!
91//! # Persistence
92//!
93//! Save and load event data (requires `serde` feature with a storage backend like `storage-fs`):
94//!
95#![cfg_attr(
96    feature = "serde",
97    doc = r#"
98```
99# use tiny_counter::EventStore;
100# use tiny_counter::storage::MemoryStorage;
101let mut store = EventStore::builder()
102    .with_storage(MemoryStorage::new())
103    .build()
104    .unwrap();
105
106store.record("event");
107store.persist().unwrap();
108```
109"#
110)]
111#![cfg_attr(
112    not(feature = "serde"),
113    doc = r#"
114```text
115See examples/persistence.rs for usage with serde feature enabled.
116```
117"#
118)]
119//!
120
121mod clock;
122#[doc(hidden)]
123pub mod config_converter;
124mod count_ring;
125mod counter;
126mod error;
127pub mod formatter;
128mod interval;
129pub mod storage;
130mod store;
131mod sync_time;
132mod time_unit;
133mod traits;
134
135pub use formatter::Formatter;
136
137pub use clock::{SystemClock, TestClock};
138pub use error::{Error, LimitExceeded, Result};
139pub use store::builder::EventStoreBuilder;
140pub use store::limiter::{Constraint, LimitUsage, Limiter, Reservation, Schedule};
141pub use store::query::{
142    DeltaQuery, DeltaRangeQuery, MultiQuery, MultiRangeQuery, Query, RangeQuery, RatioQuery,
143};
144pub use store::{EventId, EventStore};
145pub use time_unit::{TimeUnit, TimeWindow};
146pub use traits::{Clock, Storage};
147
148/// Internal APIs exposed for benchmarking only.
149///
150/// **WARNING:** These APIs have no stability guarantees and may change
151/// or be removed without notice. Do not use in production code.
152///
153/// This module exists to support performance benchmarking while clearly
154/// signaling that these are internal implementation details.
155#[doc(hidden)]
156pub mod internal {
157    pub use crate::config_converter;
158    pub use crate::counter::config::EventCounterConfig;
159    pub use crate::counter::SingleEventCounter;
160    pub use crate::interval::config::IntervalConfig;
161    pub use crate::sync_time::synchronized_start;
162}
163
164// Internal re-exports for crate use
165pub(crate) use count_ring::CountRing;
166pub(crate) use counter::config::EventCounterConfig;
167pub(crate) use counter::SingleEventCounter;
168pub(crate) use interval::config::IntervalConfig;
169pub(crate) use interval::IntervalCounter;
170pub(crate) use store::inner::EventStoreInner;