1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::{DistkitError, RedisKey};
/// Async interface for distributed counter operations.
///
/// Both [`StrictCounter`](super::StrictCounter) and
/// [`LaxCounter`](super::LaxCounter) implement this trait, so generic code
/// can work with either.
#[async_trait::async_trait]
pub trait CounterTrait {
/// Increments the counter by `count` and returns the new total.
///
/// A negative `count` decrements. Passing `0` returns the current value
/// without modification.
///
/// # Examples
///
/// ```rust
/// # use distkit::{RedisKey, counter::CounterTrait};
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let counter = distkit::__doctest_helpers::strict_counter().await?;
/// let key = RedisKey::try_from("visits".to_string())?;
/// assert_eq!(counter.inc(&key, 1).await?, 1);
/// assert_eq!(counter.inc(&key, 9).await?, 10);
/// // Negative count is the same as calling dec.
/// assert_eq!(counter.inc(&key, -3).await?, 7);
/// # Ok(())
/// # }
/// ```
async fn inc(&self, key: &RedisKey, count: i64) -> Result<i64, DistkitError>;
/// Decrements the counter by `count` and returns the new total.
///
/// Equivalent to `inc(key, -count)`. Counters can go negative.
///
/// # Examples
///
/// ```rust
/// # use distkit::{RedisKey, counter::CounterTrait};
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let counter = distkit::__doctest_helpers::strict_counter().await?;
/// let key = RedisKey::try_from("tokens".to_string())?;
/// counter.set(&key, 10).await?;
/// assert_eq!(counter.dec(&key, 3).await?, 7);
/// // Counters can go negative.
/// assert_eq!(counter.dec(&key, 100).await?, -93);
/// # Ok(())
/// # }
/// ```
async fn dec(&self, key: &RedisKey, count: i64) -> Result<i64, DistkitError>;
/// Returns the current value of the counter, or `0` if the key does not
/// exist.
///
/// # Examples
///
/// ```rust
/// # use distkit::{RedisKey, counter::CounterTrait};
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let counter = distkit::__doctest_helpers::strict_counter().await?;
/// let key = RedisKey::try_from("visits".to_string())?;
/// // A key that does not exist returns 0.
/// assert_eq!(counter.get(&key).await?, 0);
/// counter.inc(&key, 5).await?;
/// assert_eq!(counter.get(&key).await?, 5);
/// # Ok(())
/// # }
/// ```
async fn get(&self, key: &RedisKey) -> Result<i64, DistkitError>;
/// Sets the counter to an exact value, overwriting any previous state.
/// Returns the value that was set.
///
/// # Examples
///
/// ```rust
/// # use distkit::{RedisKey, counter::CounterTrait};
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let counter = distkit::__doctest_helpers::strict_counter().await?;
/// let key = RedisKey::try_from("inventory".to_string())?;
/// counter.inc(&key, 1000).await?;
/// // Overwrite with an authoritative count.
/// assert_eq!(counter.set(&key, 850).await?, 850);
/// assert_eq!(counter.get(&key).await?, 850);
/// # Ok(())
/// # }
/// ```
async fn set(&self, key: &RedisKey, count: i64) -> Result<i64, DistkitError>;
/// Deletes the counter and returns the value it held before deletion.
/// Returns `0` if the key did not exist.
///
/// # Examples
///
/// ```rust
/// # use distkit::{RedisKey, counter::CounterTrait};
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let counter = distkit::__doctest_helpers::strict_counter().await?;
/// let key = RedisKey::try_from("session".to_string())?;
/// counter.set(&key, 42).await?;
/// assert_eq!(counter.del(&key).await?, 42);
/// // After deletion the key reads back as 0.
/// assert_eq!(counter.get(&key).await?, 0);
/// // Deleting a non-existent key returns 0.
/// assert_eq!(counter.del(&key).await?, 0);
/// # Ok(())
/// # }
/// ```
async fn del(&self, key: &RedisKey) -> Result<i64, DistkitError>;
/// Removes all counters under the current prefix.
///
/// # Examples
///
/// ```rust
/// # use distkit::{RedisKey, counter::CounterTrait};
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let counter = distkit::__doctest_helpers::strict_counter().await?;
/// let k1 = RedisKey::try_from("a".to_string())?;
/// let k2 = RedisKey::try_from("b".to_string())?;
/// counter.set(&k1, 10).await?;
/// counter.set(&k2, 20).await?;
/// counter.clear().await?;
/// assert_eq!(counter.get(&k1).await?, 0);
/// assert_eq!(counter.get(&k2).await?, 0);
/// # Ok(())
/// # }
/// ```
async fn clear(&self) -> Result<(), DistkitError>;
}