otp_std/
skew.rs

1//! Time-based One-Time Password (TOTP) skews.
2
3use std::{fmt, iter::once, str::FromStr};
4
5use miette::Diagnostic;
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10use thiserror::Error;
11
12use crate::{
13    int::{self, ParseError},
14    macros::errors,
15};
16
17/// The disabled skew value.
18pub const DISABLED: u64 = 0;
19
20/// The default skew value.
21pub const DEFAULT: u64 = 1;
22
23/// Represents errors that can occur when parsing skews.
24#[derive(Debug, Error, Diagnostic)]
25#[error("failed to parse `{string}` to skew")]
26#[diagnostic(code(otp_std::skew), help("see the report for more information"))]
27pub struct Error {
28    /// The source of this error.
29    #[source]
30    #[diagnostic_source]
31    pub source: ParseError,
32    /// The string that could not be parsed.
33    pub string: String,
34}
35
36impl Error {
37    /// Constructs [`Self`].
38    pub const fn new(source: ParseError, string: String) -> Self {
39        Self { source, string }
40    }
41}
42
43/// Represents value skews (see [`apply`] for more information).
44///
45/// [`apply`]: Self::apply
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48#[cfg_attr(feature = "serde", serde(from = "u64", into = "u64"))]
49pub struct Skew {
50    value: u64,
51}
52
53errors! {
54    Type = Self::Err,
55    Hack = $,
56    error => new(error, string => to_owned),
57}
58
59impl FromStr for Skew {
60    type Err = Error;
61
62    fn from_str(string: &str) -> Result<Self, Self::Err> {
63        let value = string
64            .parse()
65            .map_err(|error| error!(int::wrap(error), string))?;
66
67        Ok(Self::new(value))
68    }
69}
70
71impl fmt::Display for Skew {
72    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
73        self.get().fmt(formatter)
74    }
75}
76
77impl From<u64> for Skew {
78    fn from(value: u64) -> Self {
79        Self::new(value)
80    }
81}
82
83impl From<Skew> for u64 {
84    fn from(skew: Skew) -> Self {
85        skew.get()
86    }
87}
88
89impl Default for Skew {
90    fn default() -> Self {
91        Self::DEFAULT
92    }
93}
94
95impl Skew {
96    /// Constructs [`Self`].
97    pub const fn new(value: u64) -> Self {
98        Self { value }
99    }
100
101    /// Returns the value wrapped in [`Self`].
102    pub const fn get(self) -> u64 {
103        self.value
104    }
105
106    /// Returns the disabled [`Self`].
107    pub const fn disabled() -> Self {
108        Self::DISABLED
109    }
110
111    /// Applies the skew to the given value.
112    ///
113    /// Given some skew `s` and value `n`, this method returns an iterator that yields
114    ///
115    /// ```text
116    /// n - s, n - s + 1, ..., n - 1, n, n + 1, ..., n + s - 1, n + s
117    /// ```
118    ///
119    /// # Note
120    ///
121    /// In case of overflows, the iterator will skip the values that would cause them.
122    ///
123    /// # Examples
124    ///
125    /// Using zero to only accept the *exact* value:
126    ///
127    /// ```
128    /// use otp_std::Skew;
129    ///
130    /// let skew = Skew::new(0);
131    ///
132    /// let mut values = skew.apply(13);
133    ///
134    /// assert_eq!(values.next(), Some(13));
135    /// assert_eq!(values.next(), None);
136    /// ```
137    ///
138    /// Using one:
139    ///
140    /// ```
141    /// use otp_std::Skew;
142    ///
143    /// let skew = Skew::new(1);
144    ///
145    /// let mut values = skew.apply(13);
146    ///
147    /// assert_eq!(values.next(), Some(12));
148    /// assert_eq!(values.next(), Some(13));
149    /// assert_eq!(values.next(), Some(14));
150    /// assert_eq!(values.next(), None);
151    /// ```
152    ///
153    /// Overflow handling:
154    ///
155    /// ```rust
156    /// use otp_std::Skew;
157    ///
158    /// let skew = Skew::new(1);
159    ///
160    /// let value = u64::MAX;
161    ///
162    /// let mut values = skew.apply(value);
163    ///
164    /// assert_eq!(values.next(), Some(value - 1));
165    /// assert_eq!(values.next(), Some(value));
166    /// assert_eq!(values.next(), None);
167    /// ```
168    pub fn apply(self, value: u64) -> impl Iterator<Item = u64> {
169        let sub = (1..=self.get()).filter_map(move |offset| value.checked_sub(offset));
170
171        let add = (1..=self.get()).filter_map(move |offset| value.checked_add(offset));
172
173        sub.rev().chain(once(value)).chain(add)
174    }
175
176    /// The disabled [`Self`] value.
177    pub const DISABLED: Self = Self::new(DISABLED);
178
179    /// The default [`Self`] value.
180    pub const DEFAULT: Self = Self::new(DEFAULT);
181}