1use core::fmt;
2
3#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5#[non_exhaustive]
6pub enum Error {
7 SecretTooShort {
9 actual: usize,
11 minimum: usize,
13 },
14 InvalidDigits {
16 actual: u8,
18 },
19 ZeroPeriod,
21 TimestampBeforeEpoch {
23 timestamp: u64,
25 epoch: u64,
27 },
28}
29
30impl fmt::Display for Error {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::SecretTooShort { actual, minimum } => write!(
34 f,
35 "shared secret is {actual} bytes; RFC 4226 requires at least {minimum}"
36 ),
37 Self::InvalidDigits { actual } => {
38 write!(f, "invalid OTP width {actual}; expected 6, 7, or 8 digits")
39 }
40 Self::ZeroPeriod => f.write_str("TOTP period must be non-zero"),
41 Self::TimestampBeforeEpoch { timestamp, epoch } => write!(
42 f,
43 "Unix timestamp {timestamp} precedes the configured TOTP epoch {epoch}"
44 ),
45 }
46 }
47}
48
49#[cfg(feature = "std")]
50impl std::error::Error for Error {}
51
52#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54#[non_exhaustive]
55pub enum CodeError {
56 InvalidLength {
58 actual: usize,
60 expected: u8,
62 },
63 NonDecimal {
65 index: usize,
67 },
68}
69
70impl fmt::Display for CodeError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 Self::InvalidLength { actual, expected } => {
74 write!(f, "OTP is {actual} bytes; expected exactly {expected}")
75 }
76 Self::NonDecimal { index } => {
77 write!(f, "OTP contains a non-decimal byte at index {index}")
78 }
79 }
80 }
81}
82
83#[cfg(feature = "std")]
84impl std::error::Error for CodeError {}