otp_std/
counter.rs

1//! One-Time Password (OTP) counters.
2
3use std::{fmt, str::FromStr};
4
5use const_macros::const_none;
6
7use miette::Diagnostic;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use thiserror::Error;
13
14use crate::{
15    int::{self, ParseError},
16    macros::errors,
17};
18
19/// The default counter value.
20pub const DEFAULT: u64 = 0;
21
22/// Represents errors that can occur when parsing counters.
23#[derive(Debug, Error, Diagnostic)]
24#[error("failed to parse `{string}` to counter")]
25#[diagnostic(code(otp_std::counter), help("see the report for more information"))]
26pub struct Error {
27    /// The source of this error.
28    #[source]
29    #[diagnostic_source]
30    pub source: ParseError,
31    /// The string that could not be parsed.
32    pub string: String,
33}
34
35impl Error {
36    /// Constructs [`Self`].
37    pub const fn new(source: ParseError, string: String) -> Self {
38        Self { source, string }
39    }
40}
41
42/// Represents counters.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45#[cfg_attr(feature = "serde", serde(from = "u64", into = "u64"))]
46pub struct Counter {
47    value: u64,
48}
49
50errors! {
51    Type = Error,
52    Hack = $,
53    error => new(error, string => to_owned),
54}
55
56impl FromStr for Counter {
57    type Err = Error;
58
59    fn from_str(string: &str) -> Result<Self, Self::Err> {
60        let value = string
61            .parse()
62            .map_err(|error| error!(int::wrap(error), string))?;
63
64        Ok(Self::new(value))
65    }
66}
67
68impl fmt::Display for Counter {
69    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70        self.get().fmt(formatter)
71    }
72}
73
74impl From<u64> for Counter {
75    fn from(value: u64) -> Self {
76        Self::new(value)
77    }
78}
79
80impl From<Counter> for u64 {
81    fn from(counter: Counter) -> Self {
82        counter.get()
83    }
84}
85
86impl Default for Counter {
87    fn default() -> Self {
88        Self::DEFAULT
89    }
90}
91
92/// The message used for counter overflow panics.
93pub const OVERFLOW: &str = "overflow";
94
95impl Counter {
96    /// Constructs [`Self`].
97    pub const fn new(value: u64) -> Self {
98        Self { value }
99    }
100
101    /// Returns the value of this counter.
102    pub const fn get(self) -> u64 {
103        self.value
104    }
105
106    /// Returns the incremented counter while checking for overflows.
107    ///
108    /// # Note
109    ///
110    /// Since [`Counter`] is [`Copy`], one can continue using the original counter after calling
111    /// this method. So, even if [`None`] is returned, the original counter will not be dropped.
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// use otp_std::Counter;
117    ///
118    /// let counter = Counter::new(0);
119    /// let expected = Counter::new(1);
120    ///
121    /// assert_eq!(counter.try_next(), Some(expected));
122    /// ```
123    ///
124    /// Returning [`None`] on overflows:
125    ///
126    /// ```
127    /// use otp_std::Counter;
128    ///
129    /// let counter = Counter::new(u64::MAX);
130    ///
131    /// assert_eq!(counter.try_next(), None);
132    /// ```
133    #[must_use = "this method returns the incremented counter instead of modifying the original"]
134    pub const fn try_next(self) -> Option<Self> {
135        let value = const_none!(self.get().checked_add(1));
136
137        Some(Self::new(value))
138    }
139
140    /// Returns the incremented counter, panicking on overflows.
141    ///
142    /// # Panics
143    ///
144    /// This method will panic if the counter overflows.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use otp_std::Counter;
150    ///
151    /// let counter = Counter::new(0);
152    /// let expected = Counter::new(1);
153    ///
154    /// assert_eq!(counter.next(), expected);
155    /// ```
156    ///
157    /// Panicking on overflows:
158    ///
159    /// ```should_panic
160    /// use otp_std::Counter;
161    ///
162    /// let counter = Counter::new(u64::MAX);
163    ///
164    /// counter.next();
165    /// ```
166    #[must_use = "this method returns the incremented counter instead of modifying the original"]
167    pub const fn next(self) -> Self {
168        self.try_next().expect(OVERFLOW)
169    }
170
171    /// The default [`Self`] value.
172    pub const DEFAULT: Self = Self::new(DEFAULT);
173}